Skip to content

Commit b43416d

Browse files
Merge pull request #187 from racheljpg/gcpclustergen
OCPCLOUD-2205: Generate GCPCluster resource for CAPI cluster
2 parents 88e9534 + 2a66482 commit b43416d

File tree

6 files changed

+148
-57
lines changed

6 files changed

+148
-57
lines changed

cmd/cluster-capi-operator/main.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ import (
4949
crwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
5050

5151
configv1 "github.com/openshift/api/config/v1"
52-
mapiv1 "github.com/openshift/api/machine/v1beta1"
52+
mapiv1 "github.com/openshift/api/machine/v1"
53+
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
5354
"github.com/openshift/cluster-capi-operator/pkg/controllers"
5455
"github.com/openshift/cluster-capi-operator/pkg/controllers/capiinstaller"
5556
"github.com/openshift/cluster-capi-operator/pkg/controllers/cluster"
@@ -81,6 +82,7 @@ func initScheme(scheme *runtime.Scheme) {
8182
utilruntime.Must(ibmpowervsv1.AddToScheme(scheme))
8283
utilruntime.Must(vspherev1.AddToScheme(scheme))
8384
utilruntime.Must(mapiv1.AddToScheme(scheme))
85+
utilruntime.Must(mapiv1beta1.AddToScheme(scheme))
8486
}
8587

8688
//nolint:funlen

e2e/gcp_test.go

-48
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ var _ = Describe("Cluster API GCP MachineSet", Ordered, func() {
3333
}
3434
framework.CreateCoreCluster(cl, clusterName, "GCPCluster")
3535
mapiMachineSpec = getGCPMAPIProviderSpec(cl)
36-
createGCPCluster(cl, mapiMachineSpec)
3736
})
3837

3938
AfterEach(func() {
@@ -80,53 +79,6 @@ func getGCPMAPIProviderSpec(cl client.Client) *mapiv1.GCPMachineProviderSpec {
8079
return providerSpec
8180
}
8281

83-
func createGCPCluster(cl client.Client, mapiProviderSpec *mapiv1.GCPMachineProviderSpec) *gcpv1.GCPCluster {
84-
By("Creating GCP cluster")
85-
86-
gcpCluster := &gcpv1.GCPCluster{
87-
ObjectMeta: metav1.ObjectMeta{
88-
Name: clusterName,
89-
Namespace: framework.CAPINamespace,
90-
// The ManagedBy Annotation is set so CAPI infra providers ignore the InfraCluster object,
91-
// as that's managed externally, in this case by the cluster-capi-operator's infracluster controller.
92-
Annotations: map[string]string{
93-
clusterv1.ManagedByAnnotation: managedByAnnotationValueClusterCAPIOperatorInfraClusterController,
94-
},
95-
},
96-
Spec: gcpv1.GCPClusterSpec{
97-
Network: gcpv1.NetworkSpec{
98-
Name: &mapiProviderSpec.NetworkInterfaces[0].Network,
99-
},
100-
Region: mapiProviderSpec.Region,
101-
Project: mapiProviderSpec.ProjectID,
102-
},
103-
}
104-
105-
if err := cl.Create(ctx, gcpCluster); err != nil && !apierrors.IsAlreadyExists(err) {
106-
Expect(err).ToNot(HaveOccurred())
107-
}
108-
109-
Eventually(func() (bool, error) {
110-
patchedGCPCluster := &gcpv1.GCPCluster{}
111-
err := cl.Get(ctx, client.ObjectKeyFromObject(gcpCluster), patchedGCPCluster)
112-
if err != nil {
113-
return false, err
114-
}
115-
116-
if patchedGCPCluster.Annotations == nil {
117-
return false, nil
118-
}
119-
120-
if _, ok := patchedGCPCluster.Annotations[clusterv1.ManagedByAnnotation]; !ok {
121-
return false, nil
122-
}
123-
124-
return patchedGCPCluster.Status.Ready, nil
125-
}, framework.WaitShort).Should(BeTrue())
126-
127-
return gcpCluster
128-
}
129-
13082
func createGCPMachineTemplate(cl client.Client, mapiProviderSpec *mapiv1.GCPMachineProviderSpec) *gcpv1.GCPMachineTemplate {
13183
By("Creating GCP machine template")
13284

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ require (
4646
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240531134648-6636df17d67b
4747
)
4848

49-
require sigs.k8s.io/yaml v1.4.0 // indirect
49+
require sigs.k8s.io/yaml v1.4.0
5050

5151
require (
5252
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect

pkg/controllers/infracluster/gcp.go

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright 2024 Red Hat, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package infracluster
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"net/url"
22+
"strconv"
23+
24+
"github.com/go-logr/logr"
25+
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
26+
cerrors "k8s.io/apimachinery/pkg/api/errors"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
gcpv1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
29+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
30+
"sigs.k8s.io/controller-runtime/pkg/client"
31+
"sigs.k8s.io/yaml"
32+
)
33+
34+
// ensureGCPCluster ensures the GCPCluster cluster object exists.
35+
//
36+
//nolint:funlen
37+
func (r *InfraClusterController) ensureGCPCluster(ctx context.Context, log logr.Logger) (client.Object, error) {
38+
target := &gcpv1.GCPCluster{ObjectMeta: metav1.ObjectMeta{
39+
Name: r.Infra.Status.InfrastructureName,
40+
Namespace: defaultCAPINamespace,
41+
}}
42+
43+
// Checking whether InfraCluster object exists. If it doesn't, create it.
44+
if err := r.Get(ctx, client.ObjectKeyFromObject(target), target); err != nil && !cerrors.IsNotFound(err) {
45+
return nil, fmt.Errorf("failed to get InfraCluster: %w", err)
46+
} else if err == nil {
47+
return target, nil
48+
}
49+
50+
log.Info(fmt.Sprintf("GCPCluster %s/%s does not exist, creating it", target.Namespace, target.Name))
51+
52+
apiURL, err := url.Parse(r.Infra.Status.APIServerInternalURL)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to parse apiUrl: %w", err)
55+
}
56+
57+
port, err := strconv.ParseInt(apiURL.Port(), 10, 32)
58+
if err != nil {
59+
return nil, fmt.Errorf("failed to parse apiUrl port: %w", err)
60+
}
61+
62+
if r.Infra.Status.PlatformStatus == nil {
63+
return nil, fmt.Errorf("infrastructure PlatformStatus should not be nil: %w", err)
64+
}
65+
66+
gcpProjectID, err := r.getGCPProjectID(ctx)
67+
if err != nil {
68+
return nil, fmt.Errorf("error obtaining GCP Project ID: %w", err)
69+
}
70+
71+
providerSpec, err := getGCPMAPIProviderSpec(ctx, r.Client)
72+
if err != nil {
73+
return nil, fmt.Errorf("error obtaining GCP Provider Spec: %w", err)
74+
}
75+
76+
target = &gcpv1.GCPCluster{
77+
ObjectMeta: metav1.ObjectMeta{
78+
Name: r.Infra.Status.InfrastructureName,
79+
Namespace: defaultCAPINamespace,
80+
// The ManagedBy Annotation is set so CAPI infra providers ignore the InfraCluster object,
81+
// as that's managed externally, in this case by this controller.
82+
Annotations: map[string]string{
83+
clusterv1.ManagedByAnnotation: managedByAnnotationValueClusterCAPIOperatorInfraClusterController,
84+
},
85+
},
86+
Spec: gcpv1.GCPClusterSpec{
87+
Network: gcpv1.NetworkSpec{
88+
Name: &providerSpec.NetworkInterfaces[0].Network,
89+
},
90+
Region: r.Infra.Status.PlatformStatus.GCP.Region,
91+
Project: gcpProjectID,
92+
ControlPlaneEndpoint: clusterv1.APIEndpoint{
93+
Host: apiURL.Hostname(),
94+
Port: int32(port),
95+
},
96+
},
97+
}
98+
99+
if err := r.Create(ctx, target); err != nil {
100+
return nil, fmt.Errorf("failed to create InfraCluster: %w", err)
101+
}
102+
103+
log.Info(fmt.Sprintf("InfraCluster '%s/%s' successfully created", defaultCAPINamespace, r.Infra.Status.InfrastructureName))
104+
105+
return target, nil
106+
}
107+
108+
// getGCPMAPIProviderSpec returns a GCP Machine ProviderSpec from the the cluster.
109+
func getGCPMAPIProviderSpec(ctx context.Context, cl client.Client) (*mapiv1beta1.GCPMachineProviderSpec, error) {
110+
rawProviderSpec, err := getRawMAPIProviderSpec(ctx, cl)
111+
if err != nil {
112+
return nil, fmt.Errorf("unable to obtain MAPI ProviderSpec: %w", err)
113+
}
114+
115+
providerSpec := &mapiv1beta1.GCPMachineProviderSpec{}
116+
if err := yaml.Unmarshal(rawProviderSpec, providerSpec); err != nil {
117+
return nil, fmt.Errorf("unable to unmarshal MAPI ProviderSpec: %w", err)
118+
}
119+
120+
return providerSpec, nil
121+
}
122+
123+
// getGCPProjectID obtains the GCP Project ID.
124+
func (r *InfraClusterController) getGCPProjectID(ctx context.Context) (string, error) {
125+
if r.Infra.Spec.PlatformSpec.GCP == nil || len(r.Infra.Status.PlatformStatus.GCP.ProjectID) == 0 {
126+
// Devise GCP Project ID via MAPI providerSpec.
127+
machineSpec, err := getGCPMAPIProviderSpec(ctx, r.Client)
128+
if err != nil || machineSpec == nil {
129+
return "", fmt.Errorf("unable to get GCP MAPI ProviderSpec: %w", err)
130+
}
131+
132+
return machineSpec.ProjectID, nil
133+
}
134+
135+
projectID := r.Infra.Status.PlatformStatus.GCP.ProjectID
136+
137+
return projectID, nil
138+
}

pkg/controllers/infracluster/infracluster_controller.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"sigs.k8s.io/controller-runtime/pkg/client"
3636
"sigs.k8s.io/controller-runtime/pkg/handler"
3737

38-
gcpv1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
3938
ibmpowervsv1 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2"
4039
openstackv1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
4140

@@ -188,12 +187,12 @@ func (r *InfraClusterController) ensureInfraCluster(ctx context.Context, log log
188187
return nil, fmt.Errorf("error ensuring AWSCluster: %w", err)
189188
}
190189
case configv1.GCPPlatformType:
191-
gcpCluster := &gcpv1.GCPCluster{}
192-
if err := r.Get(ctx, client.ObjectKey{Namespace: defaultCAPINamespace, Name: r.Infra.Status.InfrastructureName}, gcpCluster); err != nil && !kerrors.IsNotFound(err) {
193-
return nil, fmt.Errorf("error getting InfraCluster object: %w", err)
194-
}
190+
var err error
195191

196-
infraCluster = gcpCluster
192+
infraCluster, err = r.ensureGCPCluster(ctx, log)
193+
if err != nil {
194+
return nil, fmt.Errorf("error ensuring GCPCluster: %w", err)
195+
}
197196
case configv1.AzurePlatformType:
198197
azureCluster := &azurev1.AzureCluster{}
199198
if err := r.Get(ctx, client.ObjectKey{Namespace: defaultCAPINamespace, Name: r.Infra.Status.InfrastructureName}, azureCluster); err != nil && !kerrors.IsNotFound(err) {

pkg/controllers/infracluster/vsphere.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import (
2424

2525
"github.com/go-logr/logr"
2626
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
27-
"gopkg.in/yaml.v2"
2827
corev1 "k8s.io/api/core/v1"
2928
cerrors "k8s.io/apimachinery/pkg/api/errors"
3029
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3130
"k8s.io/apimachinery/pkg/types"
3231
vspherev1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1"
3332
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3433
"sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/yaml"
3535
)
3636

3737
var (

0 commit comments

Comments
 (0)