@@ -5,17 +5,29 @@ import (
55 "fmt"
66 "path/filepath"
77 "strings"
8+ "time"
89
910 "github.com/pkg/errors"
1011 "github.com/sirupsen/logrus"
12+ "gopkg.in/yaml.v2"
13+ apierrors "k8s.io/apimachinery/pkg/api/errors"
14+ "k8s.io/apimachinery/pkg/util/wait"
15+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
16+ utilkubeconfig "sigs.k8s.io/cluster-api/util/kubeconfig"
17+ "sigs.k8s.io/controller-runtime/pkg/client"
1118
1219 "github.com/openshift/installer/pkg/asset"
1320 "github.com/openshift/installer/pkg/asset/cluster/aws"
1421 "github.com/openshift/installer/pkg/asset/cluster/azure"
1522 "github.com/openshift/installer/pkg/asset/cluster/openstack"
1623 "github.com/openshift/installer/pkg/asset/installconfig"
24+ awsconfig "github.com/openshift/installer/pkg/asset/installconfig/aws"
25+ "github.com/openshift/installer/pkg/asset/kubeconfig"
26+ "github.com/openshift/installer/pkg/asset/manifests/capiutils"
27+ capimanifests "github.com/openshift/installer/pkg/asset/manifests/clusterapi"
1728 "github.com/openshift/installer/pkg/asset/password"
1829 "github.com/openshift/installer/pkg/asset/quota"
30+ "github.com/openshift/installer/pkg/clusterapi"
1931 infra "github.com/openshift/installer/pkg/infrastructure/platform"
2032 typesaws "github.com/openshift/installer/pkg/types/aws"
2133 typesazure "github.com/openshift/installer/pkg/types/azure"
@@ -56,6 +68,8 @@ func (c *Cluster) Dependencies() []asset.Asset {
5668 & quota.PlatformQuotaCheck {},
5769 & TerraformVariables {},
5870 & password.KubeadminPassword {},
71+ & capimanifests.Cluster {},
72+ & kubeconfig.AdminClient {},
5973 }
6074}
6175
@@ -82,6 +96,16 @@ func (c *Cluster) Generate(parents asset.Parents) (err error) {
8296 return errors .New ("cluster cannot be created with bootstrapInPlace set" )
8397 }
8498
99+ // Check if we're using Cluster API.
100+ if capiutils .IsEnabled (installConfig ) {
101+ return c .provisionWithClusterAPI (context .TODO (), parents , installConfig , clusterID )
102+ }
103+
104+ // Otherwise, use the normal path.
105+ return c .provision (installConfig , clusterID , terraformVariables )
106+ }
107+
108+ func (c * Cluster ) provision (installConfig * installconfig.InstallConfig , clusterID * installconfig.ClusterID , terraformVariables * TerraformVariables ) error {
85109 platform := installConfig .Config .Platform .Name ()
86110
87111 if azure := installConfig .Config .Platform .Azure ; azure != nil && azure .CloudName == typesazure .StackCloud {
@@ -124,6 +148,137 @@ func (c *Cluster) Generate(parents asset.Parents) (err error) {
124148 return nil
125149}
126150
151+ func (c * Cluster ) provisionWithClusterAPI (ctx context.Context , parents asset.Parents , installConfig * installconfig.InstallConfig , clusterID * installconfig.ClusterID ) error {
152+ capiManifests := & capimanifests.Cluster {}
153+ clusterKubeconfigAsset := & kubeconfig.AdminClient {}
154+ parents .Get (
155+ capiManifests ,
156+ clusterKubeconfigAsset ,
157+ )
158+
159+ // Only need the objects--not the files.
160+ manifests := []client.Object {}
161+ for _ , m := range capiManifests .RuntimeFiles () {
162+ manifests = append (manifests , m .Object )
163+ }
164+
165+ // Run the CAPI system.
166+ capiSystem := clusterapi .System ()
167+ if err := capiSystem .Run (ctx , installConfig ); err != nil {
168+ return fmt .Errorf ("failed to run cluster api system: %w" , err )
169+ }
170+
171+ // Grab the client.
172+ cl := capiSystem .Client ()
173+
174+ // Create all the manifests and store them.
175+ for _ , m := range manifests {
176+ m .SetNamespace (capiutils .Namespace )
177+ if err := cl .Create (context .Background (), m ); err != nil {
178+ return fmt .Errorf ("failed to create manifest: %w" , err )
179+ }
180+ logrus .Infof ("Created manifest %+T, namespace=%s name=%s" , m , m .GetNamespace (), m .GetName ())
181+ }
182+
183+ // Pass cluster kubeconfig and store it in; this is usually the role of a bootstrap provider.
184+ {
185+ key := client.ObjectKey {
186+ Name : clusterID .InfraID ,
187+ Namespace : capiutils .Namespace ,
188+ }
189+ cluster := & clusterv1.Cluster {}
190+ if err := cl .Get (context .Background (), key , cluster ); err != nil {
191+ return err
192+ }
193+ // Create the secret.
194+ clusterKubeconfig := clusterKubeconfigAsset .Files ()[0 ].Data
195+ secret := utilkubeconfig .GenerateSecret (cluster , clusterKubeconfig )
196+ if err := cl .Create (context .Background (), secret ); err != nil {
197+ return err
198+ }
199+ }
200+
201+ // Wait for the load balancer to be ready by checking the control plane endpoint
202+ // on the cluster object.
203+ var cluster * clusterv1.Cluster
204+ {
205+ if err := wait .ExponentialBackoff (wait.Backoff {
206+ Duration : time .Second * 10 ,
207+ Factor : float64 (1.5 ),
208+ Steps : 32 ,
209+ }, func () (bool , error ) {
210+ c := & clusterv1.Cluster {}
211+ if err := cl .Get (context .Background (), client.ObjectKey {
212+ Name : clusterID .InfraID ,
213+ Namespace : capiutils .Namespace ,
214+ }, c ); err != nil {
215+ if apierrors .IsNotFound (err ) {
216+ return false , nil
217+ }
218+ return false , err
219+ }
220+ cluster = c
221+ return cluster .Spec .ControlPlaneEndpoint .IsValid (), nil
222+ }); err != nil {
223+ return err
224+ }
225+ if cluster == nil {
226+ return errors .New ("error occurred during load balancer ready check" )
227+ }
228+ if cluster .Spec .ControlPlaneEndpoint .Host == "" {
229+ return errors .New ("control plane endpoint is not set" )
230+ }
231+ }
232+
233+ // Run the post-provisioning steps for the platform we're on.
234+ // TODO(vincepri): The following should probably be in a separate package with a clear
235+ // interface and multiple hooks at different stages of the cluster lifecycle.
236+ switch installConfig .Config .Platform .Name () {
237+ case typesaws .Name :
238+ ssn , err := installConfig .AWS .Session (context .TODO ())
239+ if err != nil {
240+ return fmt .Errorf ("failed to create session: %w" , err )
241+ }
242+ client := awsconfig .NewClient (ssn )
243+ r53cfg := awsconfig .GetR53ClientCfg (ssn , "" )
244+ err = client .CreateOrUpdateRecord (installConfig .Config , cluster .Spec .ControlPlaneEndpoint .Host , r53cfg )
245+ if err != nil {
246+ return fmt .Errorf ("failed to create route53 records: %w" , err )
247+ }
248+ logrus .Infof ("Created Route53 records to control plane load balancer." )
249+ default :
250+ }
251+
252+ // For each manifest we created, retrieve it and store it in the asset.
253+ for _ , m := range manifests {
254+ key := client.ObjectKey {
255+ Name : m .GetName (),
256+ Namespace : m .GetNamespace (),
257+ }
258+ if err := cl .Get (context .Background (), key , m ); err != nil {
259+ return fmt .Errorf ("failed to get manifest: %w" , err )
260+ }
261+
262+ gvk , err := cl .GroupVersionKindFor (m )
263+ if err != nil {
264+ return fmt .Errorf ("failed to get GVK for manifest: %w" , err )
265+ }
266+ fileName := fmt .Sprintf ("%s-%s-%s.yaml" , gvk .Kind , m .GetNamespace (), m .GetName ())
267+ objData , err := yaml .Marshal (m )
268+ if err != nil {
269+ errMsg := fmt .Sprintf ("failed to create infrastructure manifest %s from InstallConfig" , fileName )
270+ return errors .Wrapf (err , errMsg )
271+ }
272+ c .FileList = append (c .FileList , & asset.File {
273+ Filename : fileName ,
274+ Data : objData ,
275+ })
276+ }
277+
278+ logrus .Infof ("Cluster API resources have been created. Waiting for cluster to become ready..." )
279+ return nil
280+ }
281+
127282// Files returns the FileList generated by the asset.
128283func (c * Cluster ) Files () []* asset.File {
129284 return c .FileList
0 commit comments