mirror of
https://github.com/openshift/installer.git
synced 2026-02-05 06:46:36 +01:00
This commit is an incremental step to migrate AWS API calls to AWS SDK v2. This focuses on handlers that retrieve the source or provider of credentials, for example, via shared credential file and via environment variables. Note: these logics are to determine whether the credential provider is static, which is safe to transfer to the cluster as-is in Mint and Passthrough credentialsMode.
341 lines
12 KiB
Go
341 lines
12 KiB
Go
package manifests
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/gophercloud/utils/v2/openstack/clientconfig"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/openshift/installer/pkg/asset"
|
|
"github.com/openshift/installer/pkg/asset/installconfig"
|
|
installconfigaws "github.com/openshift/installer/pkg/asset/installconfig/aws"
|
|
"github.com/openshift/installer/pkg/asset/installconfig/gcp"
|
|
"github.com/openshift/installer/pkg/asset/installconfig/ibmcloud"
|
|
"github.com/openshift/installer/pkg/asset/installconfig/ovirt"
|
|
"github.com/openshift/installer/pkg/asset/machines"
|
|
osmachine "github.com/openshift/installer/pkg/asset/machines/openstack"
|
|
openstackmanifests "github.com/openshift/installer/pkg/asset/manifests/openstack"
|
|
"github.com/openshift/installer/pkg/asset/openshiftinstall"
|
|
"github.com/openshift/installer/pkg/asset/password"
|
|
"github.com/openshift/installer/pkg/asset/rhcos"
|
|
"github.com/openshift/installer/pkg/asset/templates/content/openshift"
|
|
"github.com/openshift/installer/pkg/types"
|
|
awstypes "github.com/openshift/installer/pkg/types/aws"
|
|
azuretypes "github.com/openshift/installer/pkg/types/azure"
|
|
baremetaltypes "github.com/openshift/installer/pkg/types/baremetal"
|
|
gcptypes "github.com/openshift/installer/pkg/types/gcp"
|
|
ibmcloudtypes "github.com/openshift/installer/pkg/types/ibmcloud"
|
|
openstacktypes "github.com/openshift/installer/pkg/types/openstack"
|
|
ovirttypes "github.com/openshift/installer/pkg/types/ovirt"
|
|
powervctypes "github.com/openshift/installer/pkg/types/powervc"
|
|
vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
|
|
)
|
|
|
|
const (
|
|
openshiftManifestDir = "openshift"
|
|
)
|
|
|
|
var (
|
|
_ asset.WritableAsset = (*Openshift)(nil)
|
|
)
|
|
|
|
// Openshift generates the dependent resource manifests for openShift (as against bootkube)
|
|
type Openshift struct {
|
|
FileList []*asset.File
|
|
}
|
|
|
|
// Name returns a human friendly name for the operator
|
|
func (o *Openshift) Name() string {
|
|
return "Openshift Manifests"
|
|
}
|
|
|
|
// Dependencies returns all of the dependencies directly needed by the
|
|
// Openshift asset
|
|
func (o *Openshift) Dependencies() []asset.Asset {
|
|
return []asset.Asset{
|
|
&installconfig.InstallConfig{},
|
|
&installconfig.ClusterID{},
|
|
&password.KubeadminPassword{},
|
|
&openshiftinstall.Config{},
|
|
&FeatureGate{},
|
|
|
|
&openshift.CloudCredsSecret{},
|
|
&openshift.KubeadminPasswordSecret{},
|
|
&openshift.RoleCloudCredsSecretReader{},
|
|
&openshift.BaremetalConfig{},
|
|
new(rhcos.Image),
|
|
&openshift.AzureCloudProviderSecret{},
|
|
}
|
|
}
|
|
|
|
// Generate generates the respective operator config.yml files
|
|
//
|
|
//nolint:gocyclo
|
|
func (o *Openshift) Generate(ctx context.Context, dependencies asset.Parents) error {
|
|
installConfig := &installconfig.InstallConfig{}
|
|
clusterID := &installconfig.ClusterID{}
|
|
kubeadminPassword := &password.KubeadminPassword{}
|
|
openshiftInstall := &openshiftinstall.Config{}
|
|
featureGate := &FeatureGate{}
|
|
dependencies.Get(installConfig, kubeadminPassword, clusterID, openshiftInstall, featureGate)
|
|
var cloudCreds cloudCredsSecretData
|
|
platform := installConfig.Config.Platform.Name()
|
|
switch platform {
|
|
case awstypes.Name:
|
|
awsconfig, err := installconfigaws.GetConfigWithOptions(ctx, config.WithRegion(installConfig.AWS.Region))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
creds, err := awsconfig.Credentials.Retrieve(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to retrieve aws credentials: %w", err)
|
|
}
|
|
|
|
if !installconfigaws.IsStaticCredentials(creds) {
|
|
switch {
|
|
case installConfig.Config.CredentialsMode == "":
|
|
return errors.Errorf("AWS credentials provided by %s are not valid for default credentials mode", creds.Source)
|
|
case installConfig.Config.CredentialsMode != types.ManualCredentialsMode:
|
|
return errors.Errorf("AWS credentials provided by %s are not valid for %s credentials mode", creds.Source, installConfig.Config.CredentialsMode)
|
|
}
|
|
}
|
|
cloudCreds = cloudCredsSecretData{
|
|
AWS: &AwsCredsSecretData{
|
|
Base64encodeAccessKeyID: base64.StdEncoding.EncodeToString([]byte(creds.AccessKeyID)),
|
|
Base64encodeSecretAccessKey: base64.StdEncoding.EncodeToString([]byte(creds.SecretAccessKey)),
|
|
},
|
|
}
|
|
case azuretypes.Name:
|
|
resourceGroupName := installConfig.Config.Azure.ClusterResourceGroupName(clusterID.InfraID)
|
|
session, err := installConfig.Azure.Session()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
creds := session.Credentials
|
|
cloudCreds = cloudCredsSecretData{
|
|
Azure: &AzureCredsSecretData{
|
|
Base64encodeSubscriptionID: base64.StdEncoding.EncodeToString([]byte(creds.SubscriptionID)),
|
|
Base64encodeClientID: base64.StdEncoding.EncodeToString([]byte(creds.ClientID)),
|
|
Base64encodeClientSecret: base64.StdEncoding.EncodeToString([]byte(creds.ClientSecret)),
|
|
Base64encodeTenantID: base64.StdEncoding.EncodeToString([]byte(creds.TenantID)),
|
|
Base64encodeResourcePrefix: base64.StdEncoding.EncodeToString([]byte(clusterID.InfraID)),
|
|
Base64encodeResourceGroup: base64.StdEncoding.EncodeToString([]byte(resourceGroupName)),
|
|
Base64encodeRegion: base64.StdEncoding.EncodeToString([]byte(installConfig.Config.Azure.Region)),
|
|
},
|
|
}
|
|
case gcptypes.Name:
|
|
session, err := gcp.GetSession(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
creds := session.Credentials.JSON
|
|
cloudCreds = cloudCredsSecretData{
|
|
GCP: &GCPCredsSecretData{
|
|
Base64encodeServiceAccount: base64.StdEncoding.EncodeToString(creds),
|
|
},
|
|
}
|
|
case ibmcloudtypes.Name:
|
|
client, err := ibmcloud.NewClient(installConfig.Config.Platform.IBMCloud.ServiceEndpoints)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cloudCreds = cloudCredsSecretData{
|
|
IBMCloud: &IBMCloudCredsSecretData{
|
|
Base64encodeAPIKey: base64.StdEncoding.EncodeToString([]byte(client.GetAPIKey())),
|
|
},
|
|
}
|
|
case openstacktypes.Name, powervctypes.Name:
|
|
opts := new(clientconfig.ClientOpts)
|
|
opts.Cloud = installConfig.Config.Platform.OpenStack.Cloud
|
|
cloud, err := clientconfig.GetCloudFromYAML(opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var caCert []byte
|
|
if cloud.CACertFile != "" {
|
|
var err error
|
|
caCert, err = os.ReadFile(cloud.CACertFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// We need to replace the local cacert path with one that is used in OpenShift
|
|
cloud.CACertFile = "/etc/kubernetes/static-pod-resources/configmaps/cloud-config/ca-bundle.pem"
|
|
}
|
|
|
|
// Application credentials are easily rotated in the event of a leak and should be preferred. Encourage their use.
|
|
authTypes := sets.New(clientconfig.AuthPassword, clientconfig.AuthV2Password, clientconfig.AuthV3Password)
|
|
if cloud.AuthInfo != nil && authTypes.Has(cloud.AuthType) {
|
|
logrus.Warnf(
|
|
"clouds.yaml file is using %q type auth. Consider using the %q auth type instead to rotate credentials more easily.",
|
|
cloud.AuthType,
|
|
clientconfig.AuthV3ApplicationCredential,
|
|
)
|
|
}
|
|
|
|
clouds := make(map[string]map[string]*clientconfig.Cloud)
|
|
clouds["clouds"] = map[string]*clientconfig.Cloud{
|
|
osmachine.CloudName: cloud,
|
|
}
|
|
|
|
marshalled, err := yaml.Marshal(clouds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cloudProviderConf, err := openstackmanifests.CloudProviderConfigSecret(cloud)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
credsEncoded := base64.StdEncoding.EncodeToString(marshalled)
|
|
cloudProviderConfEncoded := base64.StdEncoding.EncodeToString(cloudProviderConf)
|
|
caCertEncoded := base64.StdEncoding.EncodeToString(caCert)
|
|
cloudCreds = cloudCredsSecretData{
|
|
OpenStack: &OpenStackCredsSecretData{
|
|
Base64encodeCloudsYAML: credsEncoded,
|
|
Base64encodeCloudsConf: cloudProviderConfEncoded,
|
|
Base64encodeCACert: caCertEncoded,
|
|
},
|
|
}
|
|
case vspheretypes.Name:
|
|
vsphereCredList := make([]*VSphereCredsSecretData, 0)
|
|
|
|
for _, vCenter := range installConfig.Config.VSphere.VCenters {
|
|
vsphereCred := VSphereCredsSecretData{
|
|
VCenter: vCenter.Server,
|
|
Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(vCenter.Username)),
|
|
Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(vCenter.Password)),
|
|
}
|
|
vsphereCredList = append(vsphereCredList, &vsphereCred)
|
|
}
|
|
|
|
cloudCreds = cloudCredsSecretData{
|
|
VSphere: &vsphereCredList,
|
|
}
|
|
case ovirttypes.Name:
|
|
conf, err := ovirt.NewConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(conf.CABundle) == 0 && len(conf.CAFile) > 0 {
|
|
content, err := os.ReadFile(conf.CAFile)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to read the cert file: %s", conf.CAFile)
|
|
}
|
|
conf.CABundle = strings.TrimSpace(string(content))
|
|
}
|
|
|
|
cloudCreds = cloudCredsSecretData{
|
|
Ovirt: &OvirtCredsSecretData{
|
|
Base64encodeURL: base64.StdEncoding.EncodeToString([]byte(conf.URL)),
|
|
Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(conf.Username)),
|
|
Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(conf.Password)),
|
|
Base64encodeInsecure: base64.StdEncoding.EncodeToString([]byte(strconv.FormatBool(conf.Insecure))),
|
|
Base64encodeCABundle: base64.StdEncoding.EncodeToString([]byte(conf.CABundle)),
|
|
},
|
|
}
|
|
}
|
|
|
|
templateData := &openshiftTemplateData{
|
|
CloudCreds: cloudCreds,
|
|
Base64EncodedKubeadminPwHash: base64.StdEncoding.EncodeToString(kubeadminPassword.PasswordHash),
|
|
}
|
|
|
|
cloudCredsSecret := &openshift.CloudCredsSecret{}
|
|
kubeadminPasswordSecret := &openshift.KubeadminPasswordSecret{}
|
|
roleCloudCredsSecretReader := &openshift.RoleCloudCredsSecretReader{}
|
|
baremetalConfig := &openshift.BaremetalConfig{}
|
|
rhcosImage := new(rhcos.Image)
|
|
|
|
dependencies.Get(
|
|
cloudCredsSecret,
|
|
kubeadminPasswordSecret,
|
|
roleCloudCredsSecretReader,
|
|
baremetalConfig,
|
|
rhcosImage)
|
|
|
|
assetData := map[string][]byte{
|
|
"99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
|
|
}
|
|
|
|
switch platform {
|
|
case awstypes.Name, openstacktypes.Name, powervctypes.Name, vspheretypes.Name, azuretypes.Name, gcptypes.Name, ibmcloudtypes.Name, ovirttypes.Name:
|
|
if installConfig.Config.CredentialsMode != types.ManualCredentialsMode {
|
|
assetData["99_cloud-creds-secret.yaml"] = applyTemplateData(cloudCredsSecret.Files()[0].Data, templateData)
|
|
}
|
|
assetData["99_role-cloud-creds-secret-reader.yaml"] = applyTemplateData(roleCloudCredsSecretReader.Files()[0].Data, templateData)
|
|
case baremetaltypes.Name:
|
|
bmTemplateData := baremetalTemplateData{
|
|
Baremetal: installConfig.Config.Platform.BareMetal,
|
|
ProvisioningOSDownloadURL: rhcosImage.ControlPlane,
|
|
}
|
|
assetData["99_baremetal-provisioning-config.yaml"] = applyTemplateData(baremetalConfig.Files()[0].Data, bmTemplateData)
|
|
}
|
|
|
|
o.FileList = []*asset.File{}
|
|
for name, data := range assetData {
|
|
if len(data) == 0 {
|
|
continue
|
|
}
|
|
o.FileList = append(o.FileList, &asset.File{
|
|
Filename: path.Join(openshiftManifestDir, name),
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
o.FileList = append(o.FileList, openshiftInstall.Files()...)
|
|
o.FileList = append(o.FileList, featureGate.Files()...)
|
|
|
|
asset.SortFiles(o.FileList)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Files returns the files generated by the asset.
|
|
func (o *Openshift) Files() []*asset.File {
|
|
return o.FileList
|
|
}
|
|
|
|
// Load returns the openshift asset from disk.
|
|
func (o *Openshift) Load(f asset.FileFetcher) (bool, error) {
|
|
yamlFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.yaml"))
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "failed to load *.yaml files")
|
|
}
|
|
ymlFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.yml"))
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "failed to load *.yml files")
|
|
}
|
|
jsonFileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*.json"))
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "failed to load *.json files")
|
|
}
|
|
fileList := append(yamlFileList, ymlFileList...)
|
|
fileList = append(fileList, jsonFileList...)
|
|
|
|
for _, file := range fileList {
|
|
if machines.IsMachineManifest(file) {
|
|
continue
|
|
}
|
|
|
|
o.FileList = append(o.FileList, file)
|
|
}
|
|
|
|
asset.SortFiles(o.FileList)
|
|
return len(o.FileList) > 0, nil
|
|
}
|