From 8a09c88dc8b9fb4ee2330f0e8e7df39c6ab80909 Mon Sep 17 00:00:00 2001 From: Vignesh Goutham Ganesh Date: Tue, 5 Sep 2023 12:56:07 -0500 Subject: [PATCH] Image builder proxy and redhat satellite changes for RedHat Subscription Management --- projects/aws/image-builder/builder/builder.go | 37 +++++++++++++--- .../aws/image-builder/builder/constants.go | 4 +- projects/aws/image-builder/builder/types.go | 24 ++++++++++ projects/aws/image-builder/builder/utils.go | 44 ++++++++++++++++--- projects/aws/image-builder/cmd/build.go | 40 ++++++++++++++--- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/projects/aws/image-builder/builder/builder.go b/projects/aws/image-builder/builder/builder.go index 5a79111df0..df19f8f030 100644 --- a/projects/aws/image-builder/builder/builder.go +++ b/projects/aws/image-builder/builder/builder.go @@ -12,9 +12,9 @@ import ( ) var ( - eksaVersion string + eksaVersion string eksaReleaseManifest string - codebuild = os.Getenv(codebuildCIEnvVar) + codebuild = os.Getenv(codebuildCIEnvVar) ) func (b *BuildOptions) BuildImage() { @@ -29,7 +29,7 @@ func (b *BuildOptions) BuildImage() { // Clean up build tooling repo in cwd cleanup(buildToolingRepoPath) } - + gitCommitFromBundle, detectedEksaVersion, err := b.getGitCommitFromBundle() if err != nil { log.Fatalf("Error getting git commit from bundle: %v", err) @@ -62,7 +62,7 @@ func (b *BuildOptions) BuildImage() { var outputArtifactPath string var outputImageGlob []string commandEnvVars := []string{ - fmt.Sprintf("%s=%s", releaseBranchEnvVar, b.ReleaseChannel), + fmt.Sprintf("%s=%s", releaseBranchEnvVar, b.ReleaseChannel), fmt.Sprintf("%s=%s", eksAReleaseVersionEnvVar, detectedEksaVersion), fmt.Sprintf("%s=%s", eksAReleaseManifestURLEnvVar, getEksAReleasesManifestURL()), } @@ -111,6 +111,13 @@ func (b *BuildOptions) BuildImage() { commandEnvVars = append(commandEnvVars, fmt.Sprintf("%s=%s", packerAdditionalFilesConfigFileEnvVar, additionalFilesConfigFile)) } if b.Hypervisor == VSphere { + // Set proxy on RHSM if available + if b.Os == RedHat && b.VsphereConfig.HttpProxy != "" { + if err := setRhsmProxy(&b.VsphereConfig.ProxyConfig, &b.VsphereConfig.RhsmConfig); err != nil { + log.Fatalf("Error parsing proxy host and port for RHSM: %v", err) + } + } + // Read and set the vsphere connection data vsphereConfigData, err := json.Marshal(b.VsphereConfig) if err != nil { @@ -134,6 +141,8 @@ func (b *BuildOptions) BuildImage() { commandEnvVars = append(commandEnvVars, fmt.Sprintf("%s=%s", rhelUsernameEnvVar, b.VsphereConfig.RhelUsername), fmt.Sprintf("%s=%s", rhelPasswordEnvVar, b.VsphereConfig.RhelPassword), + fmt.Sprintf("%s=%s", rhsmActivationKeyEnvVar, b.VsphereConfig.ActivationKey), + fmt.Sprintf("%s=%s", rhsmOrgIDEnvVar, b.VsphereConfig.OrgId), ) } @@ -151,6 +160,13 @@ func (b *BuildOptions) BuildImage() { log.Printf("Image Build Successful\n Please find the output artifact at %s\n", outputArtifactPath) } else if b.Hypervisor == Baremetal { + // Set proxy on RHSM if available + if b.Os == RedHat && b.BaremetalConfig.HttpProxy != "" { + if err := setRhsmProxy(&b.BaremetalConfig.ProxyConfig, &b.BaremetalConfig.RhsmConfig); err != nil { + log.Fatalf("Error parsing proxy host and port for RHSM: %v", err) + } + } + baremetalConfigFile := filepath.Join(imageBuilderProjectPath, packerBaremetalConfigFile) if b.BaremetalConfig != nil { baremetalConfigData, err := json.Marshal(b.BaremetalConfig) @@ -172,6 +188,8 @@ func (b *BuildOptions) BuildImage() { commandEnvVars = append(commandEnvVars, fmt.Sprintf("%s=%s", rhelUsernameEnvVar, b.BaremetalConfig.RhelUsername), fmt.Sprintf("%s=%s", rhelPasswordEnvVar, b.BaremetalConfig.RhelPassword), + fmt.Sprintf("%s=%s", rhsmActivationKeyEnvVar, b.BaremetalConfig.ActivationKey), + fmt.Sprintf("%s=%s", rhsmOrgIDEnvVar, b.BaremetalConfig.OrgId), ) } if b.BaremetalConfig != nil { @@ -214,6 +232,13 @@ func (b *BuildOptions) BuildImage() { log.Printf("Image Build Successful\n Please find the image uploaded under Nutanix Image Service with name %s\n", b.NutanixConfig.ImageName) } else if b.Hypervisor == CloudStack { + // Set proxy on RHSM if available + if b.Os == RedHat && b.CloudstackConfig.HttpProxy != "" { + if err := setRhsmProxy(&b.CloudstackConfig.ProxyConfig, &b.CloudstackConfig.RhsmConfig); err != nil { + log.Fatalf("Error parsing proxy host and port for RHSM: %v", err) + } + } + // Create config file cloudstackConfigFile := filepath.Join(imageBuilderProjectPath, packerCloudStackConfigFile) @@ -239,6 +264,8 @@ func (b *BuildOptions) BuildImage() { commandEnvVars = append(commandEnvVars, fmt.Sprintf("%s=%s", rhelUsernameEnvVar, b.CloudstackConfig.RhelUsername), fmt.Sprintf("%s=%s", rhelPasswordEnvVar, b.CloudstackConfig.RhelPassword), + fmt.Sprintf("%s=%s", rhsmActivationKeyEnvVar, b.CloudstackConfig.ActivationKey), + fmt.Sprintf("%s=%s", rhsmOrgIDEnvVar, b.CloudstackConfig.OrgId), ) } if b.CloudstackConfig != nil { @@ -296,6 +323,6 @@ func (b *BuildOptions) BuildImage() { } cleanup(buildToolingRepoPath) - + log.Print("Build Successful. Output artifacts located at current working directory\n") } diff --git a/projects/aws/image-builder/builder/constants.go b/projects/aws/image-builder/builder/constants.go index b9d6350622..7d4a23de86 100644 --- a/projects/aws/image-builder/builder/constants.go +++ b/projects/aws/image-builder/builder/constants.go @@ -28,7 +28,7 @@ const ( prodEksaReleaseManifestURL string = "https://anywhere-assets.eks.amazonaws.com/releases/eks-a/manifest.yaml" devEksaReleaseManifestURL string = "https://dev-release-assets.eks-anywhere.model-rocket.aws.dev/eks-a-release.yaml" devBranchEksaReleaseManifestURL string = "https://dev-release-assets.eks-anywhere.model-rocket.aws.dev/%s/eks-a-release.yaml" - + // Environment variables branchNameEnvVar string = "BRANCH_NAME" codebuildCIEnvVar string = "CODEBUILD_CI" @@ -39,6 +39,8 @@ const ( packerAdditionalFilesConfigFileEnvVar string = "PACKER_ADDITIONAL_FILES_VAR_FILES" rhelUsernameEnvVar string = "RHSM_USERNAME" rhelPasswordEnvVar string = "RHSM_PASSWORD" + rhsmActivationKeyEnvVar string = "RHSM_ACTIVATION_KEY" + rhsmOrgIDEnvVar string = "RHSM_ORG_ID" packerTypeVarFilesEnvVar string = "PACKER_TYPE_VAR_FILES" eksaUseDevReleaseEnvVar string = "EKSA_USE_DEV_RELEASE" diff --git a/projects/aws/image-builder/builder/types.go b/projects/aws/image-builder/builder/types.go index d7ffc98acd..b99e6b8850 100644 --- a/projects/aws/image-builder/builder/types.go +++ b/projects/aws/image-builder/builder/types.go @@ -69,6 +69,7 @@ type VsphereConfig struct { AdditionalFiles []File `json:"files"` IsoConfig RhelConfig + ProxyConfig ExtraPackagesConfig } @@ -76,6 +77,7 @@ type BaremetalConfig struct { AdditionalFiles []File `json:"files"` IsoConfig RhelConfig + ProxyConfig ExtraPackagesConfig } @@ -83,6 +85,7 @@ type CloudstackConfig struct { AnsibleUserVars string `json:"ansible_user_vars"` IsoConfig RhelConfig + ProxyConfig ExtraPackagesConfig } @@ -95,6 +98,8 @@ type IsoConfig struct { type RhelConfig struct { RhelUsername string `json:"rhel_username"` RhelPassword string `json:"rhel_password"` + + RhsmConfig } type NutanixConfig struct { @@ -107,6 +112,7 @@ type NutanixConfig struct { NutanixUserName string `json:"nutanix_username"` NutanixPassword string `json:"nutanix_password"` NutanixSubnetName string `json:"nutanix_subnet_name"` + ProxyConfig ExtraPackagesConfig } @@ -122,6 +128,7 @@ type AMIConfig struct { VolumeSize string `json:"volume_size"` VolumeType string `json:"volume_type"` + ProxyConfig ExtraPackagesConfig } @@ -130,3 +137,20 @@ type ExtraPackagesConfig struct { ExtraRepos string `json:"extra_repos,omitempty"` ExtraRpms string `json:"extra_rpms,omitempty"` } + +type ProxyConfig struct { + HttpProxy string `json:"http_proxy,omitempty"` + HttpsProxy string `json:"https_proxy,omitempty"` + + // This can be set to a comma-delimited list of domains that should be excluded from proxying + NoProxy string `json:"no_proxy,omitempty"` +} + +type RhsmConfig struct { + ProxyHostname string `json:"rhsm_server_proxy_hostname,omitempty"` + ProxyPort string `json:"rhsm_server_proxy_port,omitempty"` + ServerHostname string `json:"rhsm_server_hostname,omitempty"` + ServerReleaseVersion string `json:"rhsm_server_release_version,omitempty"` + ActivationKey string `json:"rhsm_activation_key,omitempty"` + OrgId string `json:"rhsm_org_id,omitempty"` +} diff --git a/projects/aws/image-builder/builder/utils.go b/projects/aws/image-builder/builder/utils.go index 2aea65be0f..19a4ac7cf2 100644 --- a/projects/aws/image-builder/builder/utils.go +++ b/projects/aws/image-builder/builder/utils.go @@ -4,7 +4,9 @@ import ( "fmt" "io" "log" + "net" "net/http" + "net/url" "os" "os/exec" "path/filepath" @@ -212,20 +214,48 @@ func getEksAReleasesManifestURL() string { if eksaReleaseManifest != "" { return eksaReleaseManifest } - + return prodEksaReleaseManifestURL } - + // using a dev release, allow branch_name env var to // override manifest url branchName, ok := os.LookupEnv(branchNameEnvVar) if !ok { branchName = mainBranch - } + } if branchName != mainBranch { - return fmt.Sprintf(devBranchEksaReleaseManifestURL, branchName) - } - - return devEksaReleaseManifestURL + return fmt.Sprintf(devBranchEksaReleaseManifestURL, branchName) + } + + return devEksaReleaseManifestURL +} + +// setRhsmProxy takes the proxy config, parses it and sets the appropriate config on rhsm config +func setRhsmProxy(proxy *ProxyConfig, rhsm *RhsmConfig) error { + if proxy.HttpProxy != "" { + host, port, err := parseUrl(proxy.HttpProxy) + if err != nil { + return err + } + rhsm.ProxyHostname = host + rhsm.ProxyPort = port + } + + return nil +} + +// parseUrl takes a http endpoint and returns hostname, ports and error +func parseUrl(endpoint string) (string, string, error) { + u, err := url.Parse(endpoint) + if err != nil { + return "", "", err + } + + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + return "", "", err + } + return host, port, nil } diff --git a/projects/aws/image-builder/cmd/build.go b/projects/aws/image-builder/cmd/build.go index 586cc449c1..4318986b1b 100644 --- a/projects/aws/image-builder/cmd/build.go +++ b/projects/aws/image-builder/cmd/build.go @@ -147,7 +147,7 @@ func ValidateInputs(bo *builder.BuildOptions) error { return err } if bo.Os == builder.RedHat { - if err = validateRedhat(bo.VsphereConfig.RhelUsername, bo.VsphereConfig.RhelPassword, bo.VsphereConfig.IsoUrl); err != nil { + if err = validateRedhat(&bo.VsphereConfig.RhelConfig, bo.VsphereConfig.IsoUrl); err != nil { return err } } @@ -156,12 +156,15 @@ func ValidateInputs(bo *builder.BuildOptions) error { return err } } + if err = validateRHSM(bo.Os, &bo.VsphereConfig.RhsmConfig); err != nil { + return err + } case builder.Baremetal: if err = json.Unmarshal(config, &bo.BaremetalConfig); err != nil { return err } if bo.Os == builder.RedHat { - if err = validateRedhat(bo.BaremetalConfig.RhelUsername, bo.BaremetalConfig.RhelPassword, bo.BaremetalConfig.IsoUrl); err != nil { + if err = validateRedhat(&bo.BaremetalConfig.RhelConfig, bo.BaremetalConfig.IsoUrl); err != nil { return err } } @@ -170,6 +173,9 @@ func ValidateInputs(bo *builder.BuildOptions) error { return err } } + if err = validateRHSM(bo.Os, &bo.BaremetalConfig.RhsmConfig); err != nil { + return err + } case builder.Nutanix: if err = json.Unmarshal(config, &bo.NutanixConfig); err != nil { return err @@ -184,7 +190,7 @@ func ValidateInputs(bo *builder.BuildOptions) error { return err } if bo.Os == builder.RedHat { - if err = validateRedhat(bo.CloudstackConfig.RhelUsername, bo.CloudstackConfig.RhelPassword, bo.CloudstackConfig.IsoUrl); err != nil { + if err = validateRedhat(&bo.CloudstackConfig.RhelConfig, bo.CloudstackConfig.IsoUrl); err != nil { return err } } @@ -193,6 +199,9 @@ func ValidateInputs(bo *builder.BuildOptions) error { return err } } + if err = validateRHSM(bo.Os, &bo.CloudstackConfig.RhsmConfig); err != nil { + return err + } case builder.AMI: // Default configuration for AMI builds amiFilter := builder.DefaultUbuntu2004AMIFilterName @@ -260,9 +269,28 @@ func validateOSHypervisorCombinations(os, hypervisor string) error { return nil } -func validateRedhat(rhelUsername, rhelPassword, isoUrl string) error { - if rhelUsername == "" || rhelPassword == "" { - return fmt.Errorf("\"rhel_username\" and \"rhel_password\" are required fields in config when os is redhat") +func validateRHSM(os string, rhsmConfig *builder.RhsmConfig) error { + if rhsmConfig.ServerHostname != "" { + if os != builder.RedHat { + return fmt.Errorf("RedHat Subscription Manager Config (RHSM) cannot be provided when OS is not RedHat") + } + + if rhsmConfig.ServerReleaseVersion == "" { + return fmt.Errorf("RHSM version required when satelite server hostname is set for RHSM") + } + + if rhsmConfig.ActivationKey == "" || rhsmConfig.OrgId == "" { + return fmt.Errorf("Activation key and Org ID are required to use RHSM with satellite") + } + } + return nil +} + +func validateRedhat(rhelConfig *builder.RhelConfig, isoUrl string) error { + if rhelConfig.ServerHostname == "" { + if rhelConfig.RhelUsername == "" || rhelConfig.RhelPassword == "" { + return fmt.Errorf("\"rhel_username\" and \"rhel_password\" are required fields in config when os is redhat") + } } if isoUrl == "" { return fmt.Errorf("\"iso_url\" is a required field in config when os is redhat")