github.com/openshift/installer@v1.4.17/pkg/asset/manifests/openshift.go (about)

     1  package manifests
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/gophercloud/utils/v2/openstack/clientconfig"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  	"k8s.io/apimachinery/pkg/util/sets"
    15  	"sigs.k8s.io/yaml"
    16  
    17  	"github.com/openshift/installer/pkg/asset"
    18  	"github.com/openshift/installer/pkg/asset/installconfig"
    19  	installconfigaws "github.com/openshift/installer/pkg/asset/installconfig/aws"
    20  	"github.com/openshift/installer/pkg/asset/installconfig/gcp"
    21  	"github.com/openshift/installer/pkg/asset/installconfig/ibmcloud"
    22  	"github.com/openshift/installer/pkg/asset/installconfig/ovirt"
    23  	"github.com/openshift/installer/pkg/asset/machines"
    24  	osmachine "github.com/openshift/installer/pkg/asset/machines/openstack"
    25  	openstackmanifests "github.com/openshift/installer/pkg/asset/manifests/openstack"
    26  	"github.com/openshift/installer/pkg/asset/openshiftinstall"
    27  	"github.com/openshift/installer/pkg/asset/password"
    28  	"github.com/openshift/installer/pkg/asset/rhcos"
    29  	"github.com/openshift/installer/pkg/asset/templates/content/openshift"
    30  	"github.com/openshift/installer/pkg/types"
    31  	awstypes "github.com/openshift/installer/pkg/types/aws"
    32  	azuretypes "github.com/openshift/installer/pkg/types/azure"
    33  	baremetaltypes "github.com/openshift/installer/pkg/types/baremetal"
    34  	gcptypes "github.com/openshift/installer/pkg/types/gcp"
    35  	ibmcloudtypes "github.com/openshift/installer/pkg/types/ibmcloud"
    36  	openstacktypes "github.com/openshift/installer/pkg/types/openstack"
    37  	ovirttypes "github.com/openshift/installer/pkg/types/ovirt"
    38  	vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
    39  )
    40  
    41  const (
    42  	openshiftManifestDir = "openshift"
    43  )
    44  
    45  var (
    46  	_ asset.WritableAsset = (*Openshift)(nil)
    47  )
    48  
    49  // Openshift generates the dependent resource manifests for openShift (as against bootkube)
    50  type Openshift struct {
    51  	FileList []*asset.File
    52  }
    53  
    54  // Name returns a human friendly name for the operator
    55  func (o *Openshift) Name() string {
    56  	return "Openshift Manifests"
    57  }
    58  
    59  // Dependencies returns all of the dependencies directly needed by the
    60  // Openshift asset
    61  func (o *Openshift) Dependencies() []asset.Asset {
    62  	return []asset.Asset{
    63  		&installconfig.InstallConfig{},
    64  		&installconfig.ClusterID{},
    65  		&password.KubeadminPassword{},
    66  		&openshiftinstall.Config{},
    67  		&FeatureGate{},
    68  
    69  		&openshift.CloudCredsSecret{},
    70  		&openshift.KubeadminPasswordSecret{},
    71  		&openshift.RoleCloudCredsSecretReader{},
    72  		&openshift.BaremetalConfig{},
    73  		new(rhcos.Image),
    74  		&openshift.AzureCloudProviderSecret{},
    75  	}
    76  }
    77  
    78  // Generate generates the respective operator config.yml files
    79  //
    80  //nolint:gocyclo
    81  func (o *Openshift) Generate(ctx context.Context, dependencies asset.Parents) error {
    82  	installConfig := &installconfig.InstallConfig{}
    83  	clusterID := &installconfig.ClusterID{}
    84  	kubeadminPassword := &password.KubeadminPassword{}
    85  	openshiftInstall := &openshiftinstall.Config{}
    86  	featureGate := &FeatureGate{}
    87  	dependencies.Get(installConfig, kubeadminPassword, clusterID, openshiftInstall, featureGate)
    88  	var cloudCreds cloudCredsSecretData
    89  	platform := installConfig.Config.Platform.Name()
    90  	switch platform {
    91  	case awstypes.Name:
    92  		ssn, err := installConfig.AWS.Session(ctx)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		creds, err := ssn.Config.Credentials.Get()
    97  		if err != nil {
    98  			return err
    99  		}
   100  		if !installconfigaws.IsStaticCredentials(creds) {
   101  			switch {
   102  			case installConfig.Config.CredentialsMode == "":
   103  				return errors.Errorf("AWS credentials provided by %s are not valid for default credentials mode", creds.ProviderName)
   104  			case installConfig.Config.CredentialsMode != types.ManualCredentialsMode:
   105  				return errors.Errorf("AWS credentials provided by %s are not valid for %s credentials mode", creds.ProviderName, installConfig.Config.CredentialsMode)
   106  			}
   107  		}
   108  		cloudCreds = cloudCredsSecretData{
   109  			AWS: &AwsCredsSecretData{
   110  				Base64encodeAccessKeyID:     base64.StdEncoding.EncodeToString([]byte(creds.AccessKeyID)),
   111  				Base64encodeSecretAccessKey: base64.StdEncoding.EncodeToString([]byte(creds.SecretAccessKey)),
   112  			},
   113  		}
   114  	case azuretypes.Name:
   115  		resourceGroupName := installConfig.Config.Azure.ClusterResourceGroupName(clusterID.InfraID)
   116  		session, err := installConfig.Azure.Session()
   117  		if err != nil {
   118  			return err
   119  		}
   120  		creds := session.Credentials
   121  		cloudCreds = cloudCredsSecretData{
   122  			Azure: &AzureCredsSecretData{
   123  				Base64encodeSubscriptionID: base64.StdEncoding.EncodeToString([]byte(creds.SubscriptionID)),
   124  				Base64encodeClientID:       base64.StdEncoding.EncodeToString([]byte(creds.ClientID)),
   125  				Base64encodeClientSecret:   base64.StdEncoding.EncodeToString([]byte(creds.ClientSecret)),
   126  				Base64encodeTenantID:       base64.StdEncoding.EncodeToString([]byte(creds.TenantID)),
   127  				Base64encodeResourcePrefix: base64.StdEncoding.EncodeToString([]byte(clusterID.InfraID)),
   128  				Base64encodeResourceGroup:  base64.StdEncoding.EncodeToString([]byte(resourceGroupName)),
   129  				Base64encodeRegion:         base64.StdEncoding.EncodeToString([]byte(installConfig.Config.Azure.Region)),
   130  			},
   131  		}
   132  	case gcptypes.Name:
   133  		session, err := gcp.GetSession(ctx)
   134  		if err != nil {
   135  			return err
   136  		}
   137  		creds := session.Credentials.JSON
   138  		cloudCreds = cloudCredsSecretData{
   139  			GCP: &GCPCredsSecretData{
   140  				Base64encodeServiceAccount: base64.StdEncoding.EncodeToString(creds),
   141  			},
   142  		}
   143  	case ibmcloudtypes.Name:
   144  		client, err := ibmcloud.NewClient(installConfig.Config.Platform.IBMCloud.ServiceEndpoints)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		cloudCreds = cloudCredsSecretData{
   149  			IBMCloud: &IBMCloudCredsSecretData{
   150  				Base64encodeAPIKey: base64.StdEncoding.EncodeToString([]byte(client.GetAPIKey())),
   151  			},
   152  		}
   153  	case openstacktypes.Name:
   154  		opts := new(clientconfig.ClientOpts)
   155  		opts.Cloud = installConfig.Config.Platform.OpenStack.Cloud
   156  		cloud, err := clientconfig.GetCloudFromYAML(opts)
   157  		if err != nil {
   158  			return err
   159  		}
   160  
   161  		// We need to replace the local cacert path with one that is used in OpenShift
   162  		if cloud.CACertFile != "" {
   163  			cloud.CACertFile = "/etc/kubernetes/static-pod-resources/configmaps/cloud-config/ca-bundle.pem"
   164  		}
   165  
   166  		// Application credentials are easily rotated in the event of a leak and should be preferred. Encourage their use.
   167  		authTypes := sets.New(clientconfig.AuthPassword, clientconfig.AuthV2Password, clientconfig.AuthV3Password)
   168  		if cloud.AuthInfo != nil && authTypes.Has(cloud.AuthType) {
   169  			logrus.Warnf(
   170  				"clouds.yaml file is using %q type auth. Consider using the %q auth type instead to rotate credentials more easily.",
   171  				cloud.AuthType,
   172  				clientconfig.AuthV3ApplicationCredential,
   173  			)
   174  		}
   175  
   176  		clouds := make(map[string]map[string]*clientconfig.Cloud)
   177  		clouds["clouds"] = map[string]*clientconfig.Cloud{
   178  			osmachine.CloudName: cloud,
   179  		}
   180  
   181  		marshalled, err := yaml.Marshal(clouds)
   182  		if err != nil {
   183  			return err
   184  		}
   185  
   186  		cloudProviderConf, err := openstackmanifests.CloudProviderConfigSecret(cloud)
   187  		if err != nil {
   188  			return err
   189  		}
   190  
   191  		credsEncoded := base64.StdEncoding.EncodeToString(marshalled)
   192  		credsINIEncoded := base64.StdEncoding.EncodeToString(cloudProviderConf)
   193  		cloudCreds = cloudCredsSecretData{
   194  			OpenStack: &OpenStackCredsSecretData{
   195  				Base64encodeCloudCreds:    credsEncoded,
   196  				Base64encodeCloudCredsINI: credsINIEncoded,
   197  			},
   198  		}
   199  	case vspheretypes.Name:
   200  		vsphereCredList := make([]*VSphereCredsSecretData, 0)
   201  
   202  		for _, vCenter := range installConfig.Config.VSphere.VCenters {
   203  			vsphereCred := VSphereCredsSecretData{
   204  				VCenter:              vCenter.Server,
   205  				Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(vCenter.Username)),
   206  				Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(vCenter.Password)),
   207  			}
   208  			vsphereCredList = append(vsphereCredList, &vsphereCred)
   209  		}
   210  
   211  		cloudCreds = cloudCredsSecretData{
   212  			VSphere: &vsphereCredList,
   213  		}
   214  	case ovirttypes.Name:
   215  		conf, err := ovirt.NewConfig()
   216  		if err != nil {
   217  			return err
   218  		}
   219  
   220  		if len(conf.CABundle) == 0 && len(conf.CAFile) > 0 {
   221  			content, err := os.ReadFile(conf.CAFile)
   222  			if err != nil {
   223  				return errors.Wrapf(err, "failed to read the cert file: %s", conf.CAFile)
   224  			}
   225  			conf.CABundle = strings.TrimSpace(string(content))
   226  		}
   227  
   228  		cloudCreds = cloudCredsSecretData{
   229  			Ovirt: &OvirtCredsSecretData{
   230  				Base64encodeURL:      base64.StdEncoding.EncodeToString([]byte(conf.URL)),
   231  				Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(conf.Username)),
   232  				Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(conf.Password)),
   233  				Base64encodeInsecure: base64.StdEncoding.EncodeToString([]byte(strconv.FormatBool(conf.Insecure))),
   234  				Base64encodeCABundle: base64.StdEncoding.EncodeToString([]byte(conf.CABundle)),
   235  			},
   236  		}
   237  	}
   238  
   239  	templateData := &openshiftTemplateData{
   240  		CloudCreds:                   cloudCreds,
   241  		Base64EncodedKubeadminPwHash: base64.StdEncoding.EncodeToString(kubeadminPassword.PasswordHash),
   242  	}
   243  
   244  	cloudCredsSecret := &openshift.CloudCredsSecret{}
   245  	kubeadminPasswordSecret := &openshift.KubeadminPasswordSecret{}
   246  	roleCloudCredsSecretReader := &openshift.RoleCloudCredsSecretReader{}
   247  	baremetalConfig := &openshift.BaremetalConfig{}
   248  	rhcosImage := new(rhcos.Image)
   249  
   250  	dependencies.Get(
   251  		cloudCredsSecret,
   252  		kubeadminPasswordSecret,
   253  		roleCloudCredsSecretReader,
   254  		baremetalConfig,
   255  		rhcosImage)
   256  
   257  	assetData := map[string][]byte{
   258  		"99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
   259  	}
   260  
   261  	switch platform {
   262  	case awstypes.Name, openstacktypes.Name, vspheretypes.Name, azuretypes.Name, gcptypes.Name, ibmcloudtypes.Name, ovirttypes.Name:
   263  		if installConfig.Config.CredentialsMode != types.ManualCredentialsMode {
   264  			assetData["99_cloud-creds-secret.yaml"] = applyTemplateData(cloudCredsSecret.Files()[0].Data, templateData)
   265  		}
   266  		assetData["99_role-cloud-creds-secret-reader.yaml"] = applyTemplateData(roleCloudCredsSecretReader.Files()[0].Data, templateData)
   267  	case baremetaltypes.Name:
   268  		bmTemplateData := baremetalTemplateData{
   269  			Baremetal:                 installConfig.Config.Platform.BareMetal,
   270  			ProvisioningOSDownloadURL: rhcosImage.ControlPlane,
   271  		}
   272  		assetData["99_baremetal-provisioning-config.yaml"] = applyTemplateData(baremetalConfig.Files()[0].Data, bmTemplateData)
   273  	}
   274  
   275  	if platform == azuretypes.Name && installConfig.Config.Azure.IsARO() && installConfig.Config.CredentialsMode != types.ManualCredentialsMode {
   276  		// config is used to created compatible secret to trigger azure cloud
   277  		// controller config merge behaviour
   278  		// https://github.com/openshift/origin/blob/90c050f5afb4c52ace82b15e126efe98fa798d88/vendor/k8s.io/legacy-cloud-providers/azure/azure_config.go#L83
   279  		session, err := installConfig.Azure.Session()
   280  		if err != nil {
   281  			return err
   282  		}
   283  		config := struct {
   284  			AADClientID     string `json:"aadClientId" yaml:"aadClientId"`
   285  			AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
   286  		}{
   287  			AADClientID:     session.Credentials.ClientID,
   288  			AADClientSecret: session.Credentials.ClientSecret,
   289  		}
   290  
   291  		b, err := yaml.Marshal(config)
   292  		if err != nil {
   293  			return err
   294  		}
   295  
   296  		azureCloudProviderSecret := &openshift.AzureCloudProviderSecret{}
   297  		dependencies.Get(azureCloudProviderSecret)
   298  		for _, f := range azureCloudProviderSecret.Files() {
   299  			name := strings.TrimSuffix(filepath.Base(f.Filename), ".template")
   300  			assetData[name] = applyTemplateData(f.Data, map[string]string{
   301  				"CloudConfig": string(b),
   302  			})
   303  		}
   304  	}
   305  
   306  	o.FileList = []*asset.File{}
   307  	for name, data := range assetData {
   308  		if len(data) == 0 {
   309  			continue
   310  		}
   311  		o.FileList = append(o.FileList, &asset.File{
   312  			Filename: filepath.Join(openshiftManifestDir, name),
   313  			Data:     data,
   314  		})
   315  	}
   316  
   317  	o.FileList = append(o.FileList, openshiftInstall.Files()...)
   318  	o.FileList = append(o.FileList, featureGate.Files()...)
   319  
   320  	asset.SortFiles(o.FileList)
   321  
   322  	return nil
   323  }
   324  
   325  // Files returns the files generated by the asset.
   326  func (o *Openshift) Files() []*asset.File {
   327  	return o.FileList
   328  }
   329  
   330  // Load returns the openshift asset from disk.
   331  func (o *Openshift) Load(f asset.FileFetcher) (bool, error) {
   332  	yamlFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.yaml"))
   333  	if err != nil {
   334  		return false, errors.Wrap(err, "failed to load *.yaml files")
   335  	}
   336  	ymlFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.yml"))
   337  	if err != nil {
   338  		return false, errors.Wrap(err, "failed to load *.yml files")
   339  	}
   340  	jsonFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.json"))
   341  	if err != nil {
   342  		return false, errors.Wrap(err, "failed to load *.json files")
   343  	}
   344  	fileList := append(yamlFileList, ymlFileList...)
   345  	fileList = append(fileList, jsonFileList...)
   346  
   347  	for _, file := range fileList {
   348  		if machines.IsMachineManifest(file) {
   349  			continue
   350  		}
   351  
   352  		o.FileList = append(o.FileList, file)
   353  	}
   354  
   355  	asset.SortFiles(o.FileList)
   356  	return len(o.FileList) > 0, nil
   357  }