Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARO-11084: Helm chart generator #4872

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/install/assets/hypershift_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ type HyperShiftOperatorDeployment struct {
EnableCPOOverrides bool
AROHCPKeyVaultUsersClientID string
TechPreviewNoUpgrade bool
RegistryOverrides string
}

// String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...".
Expand Down Expand Up @@ -408,6 +409,9 @@ func (o HyperShiftOperatorDeployment) Build() *appsv1.Deployment {
if o.TechPreviewNoUpgrade {
args = append(args, fmt.Sprintf("--feature-gates=%s", featureGateString()))
}
if o.RegistryOverrides != "" {
args = append(args, fmt.Sprintf("--registry-overrides=%s", o.RegistryOverrides))
}

var volumeMounts []corev1.VolumeMount
var initVolumeMounts []corev1.VolumeMount
Expand Down
10 changes: 9 additions & 1 deletion cmd/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ type Options struct {
EnableCPOOverrides bool
AroHCPKeyVaultUsersClientID string
TechPreviewNoUpgrade bool
RegistryOverrides string
RenderNamespace bool
}

func (o *Options) Validate() error {
Expand Down Expand Up @@ -166,6 +168,7 @@ func (o *Options) Validate() error {
}

func (o *Options) ApplyDefaults() {
o.RenderNamespace = true
switch {
case o.Development:
o.HyperShiftOperatorReplicas = 0
Expand Down Expand Up @@ -241,6 +244,7 @@ func NewCommand() *cobra.Command {
cmd.PersistentFlags().BoolVar(&opts.EnableCPOOverrides, "enable-cpo-overrides", opts.EnableCPOOverrides, "If true, the HyperShift operator uses a set of static overrides for the CPO image given specific release versions")
cmd.PersistentFlags().StringVar(&opts.AroHCPKeyVaultUsersClientID, "aro-hcp-key-vault-users-client-id", opts.AroHCPKeyVaultUsersClientID, "The client ID of the managed identity which can access the Azure Key Vaults, in an AKS management cluster, to retrieve secrets and certificates.")
cmd.PersistentFlags().BoolVar(&opts.TechPreviewNoUpgrade, "tech-preview-no-upgrade", opts.TechPreviewNoUpgrade, "If true, the HyperShift operator runs with TechPreviewNoUpgrade features enabled")
cmd.PersistentFlags().StringVar(&opts.RegistryOverrides, "registry-overrides", "", "registry-overrides contains the source registry string as a key and the destination registry string as value. Images before being applied are scanned for the source registry string and if found the string is replaced with the destination registry string. Format is: sr1=dr1,sr2=dr2")

cmd.RunE = func(cmd *cobra.Command, args []string) error {
opts.ApplyDefaults()
Expand Down Expand Up @@ -280,6 +284,7 @@ func NewCommand() *cobra.Command {
}

cmd.AddCommand(NewRenderCommand(&opts))
cmd.AddCommand(NewHelmRenderCommand(&opts))

return cmd
}
Expand Down Expand Up @@ -478,7 +483,9 @@ func hyperShiftOperatorManifests(opts Options) ([]crclient.Object, []crclient.Ob
Name: opts.Namespace,
EnableOCPClusterMonitoring: opts.PlatformMonitoring.IsEnabled(),
}.Build()
objects = append(objects, operatorNamespace)
if opts.RenderNamespace {
objects = append(objects, operatorNamespace)
}

// Setup RBAC resources
operatorServiceAccount, rbacObjs := setupRBAC(opts, operatorNamespace)
Expand Down Expand Up @@ -710,6 +717,7 @@ func setupOperatorResources(opts Options, userCABundleCM *corev1.ConfigMap, trus
EnableCPOOverrides: opts.EnableCPOOverrides,
AROHCPKeyVaultUsersClientID: opts.AroHCPKeyVaultUsersClientID,
TechPreviewNoUpgrade: opts.TechPreviewNoUpgrade,
RegistryOverrides: opts.RegistryOverrides,
}.Build()
operatorService := assets.HyperShiftOperatorService{
Namespace: operatorNamespace,
Expand Down
155 changes: 155 additions & 0 deletions cmd/install/install_helm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package install

import (
"fmt"
"os"
"strings"

"github.com/openshift/hypershift/pkg/version"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
crclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var helmTemplateParams = TemplateParams{
Namespace: ".Release.Namespace",
HyperShiftImage: ".Values.image",
HyperShiftImageTag: ".Values.imagetag",
OIDCS3Name: ".Values.oidc.s3.name",
OIDCS3Region: ".Values.oidc.s3.region",
OIDCS3CredsSecret: ".Values.oidc.s3.credsSecret",
OIDCS3CredsSecretKey: ".Values.oidc.s3.credsSecretKey",
AWSPrivateRegion: ".Values.aws.private.region",
AWSPrivateCredsSecret: ".Values.aws.private.credsSecret",
AWSPrivateCredsSecretKey: ".Values.aws.private.credsSecretKey",
ExternalDNSCredsSecret: ".Values.externaldns.credsSecret",
ExternalDNSDomainFilter: ".Values.externaldns.domainFilter",
ExternalDNSTxtOwnerID: ".Values.externaldns.txtOwnerId",
ExternalDNSImage: ".Values.externaldns.image",
RegistryOverrides: ".Values.registryOverrides",
AROHCPKeyVaultUsersClientID: ".Values.azure.keyVault.clientId",
TemplateNamespace: false,
TemplateParamWrapper: func(name string) string {
return fmt.Sprintf("{{ %s }}", name)
},
}

func NewHelmRenderCommand(opts *Options) *cobra.Command {
cmd := &cobra.Command{
Use: "helm",
Short: "Generate a Helm chart for the HyperShift Operator",
SilenceUsage: true,
}

cmd.Flags().StringVar(&opts.OutputFile, "output-dir", "", "Directory to write the rendered helm chart to")

cmd.RunE = func(cmd *cobra.Command, args []string) error {
opts.ApplyDefaults()

crds, manifests, err := hyperShiftOperatorTemplateManifest(opts, helmTemplateParams)
if err != nil {
return err
}

if opts.OutputFile == "" {
opts.OutputFile = "./chart"
}
err = writeManifestsToDir(crds, fmt.Sprintf("%s/crds", opts.OutputFile))
if err != nil {
return err
}
err = writeManifestsToDir(manifests, fmt.Sprintf("%s/templates", opts.OutputFile))
if err != nil {
return err
}
err = WriteChartYaml(opts.OutputFile)
if err != nil {
return err
}
err = WriteValuesFile(opts.OutputFile)
if err != nil {
return err
}
return nil
}

return cmd
}

func WriteChartYaml(dir string) error {
data := map[string]interface{}{
"apiVersion": "v2",
"name": "hypershift-operator",
"description": "A Helm chart for the HyperShift Operator",
"type": "application",
"version": "0.1.0",
"appVersion": version.GetRevision(),
}
return writeYamlFile(fmt.Sprintf("%s/Chart.yaml", dir), data)
}

func WriteValuesFile(dir string) error {
data := map[string]interface{}{
"image": "",
"imagetag": "",
"registryOverrides": "",
"azure": map[string]interface{}{
"keyVault": map[string]interface{}{
"clientId": "",
},
},
"oidc": map[string]interface{}{
"s3": map[string]interface{}{
"name": "",
"region": "",
"credsSecret": "",
"credsSecretKey": "",
},
},
"aws": map[string]interface{}{
"private": map[string]interface{}{
"region": "",
"credsSecret": "",
"credsSecretKey": "",
},
},
}
return writeYamlFile(fmt.Sprintf("%s/values.yaml", dir), data)
}

func writeYamlFile(path string, data map[string]interface{}) error {
yamlData, err := yaml.Marshal(data)
if err != nil {
return err
}
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(yamlData)
if err != nil {
return err
}
return nil
}

func writeManifestsToDir(manifests []crclient.Object, dir string) error {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return err
}

for _, manifest := range manifests {
file, err := os.Create(fmt.Sprintf("%s/%s-%s.yaml", dir, strings.ToLower(manifest.GetObjectKind().GroupVersionKind().Kind), manifest.GetName()))
if err != nil {
return err
}
defer file.Close()
err = render([]crclient.Object{manifest}, RenderFormatYaml, file)
if err != nil {
return err
}
}
return nil
}
75 changes: 75 additions & 0 deletions cmd/install/install_helm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package install

import (
"bytes"
"io"
"os"
"testing"

"github.com/openshift/hypershift/support/testutil"
)

func ExecuteTestHelmCommand(args []string) ([]byte, error) {
// append helm to args
args = append([]string{"helm"}, args...)
cmd := NewCommand()
cmd.SetArgs(args)
b := bytes.NewBufferString("")
cmd.SetOut(b)
err := cmd.Execute()
if err != nil {
return []byte{}, err
}
return io.ReadAll(b)
}

func TestHelmCommand(t *testing.T) {
// create a folder to hold test data and delete it afterwards
tmpDir, err := os.MkdirTemp("", "test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)

_, err = ExecuteTestHelmCommand([]string{"--output-dir", tmpDir})
if err != nil {
t.Fatal(err)
}

// check if the output directory exists
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
t.Fatalf("Output directory %s does not exist", tmpDir)
}

// check if the crds directory exists ...
for _, dir := range []string{"crds", "templates"} {
dirPath := tmpDir + "/" + dir
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
t.Fatalf("%s directory %s does not exist", dir, dirPath)
}
files, err := os.ReadDir(dirPath)
if err != nil {
t.Fatal(err)
}
if len(files) == 0 {
t.Fatalf("%s directory is empty", dirPath)
}
}

// check if the Chart.yaml file exists
chartPath := tmpDir + "/Chart.yaml"
chartFileContent, err := os.ReadFile(chartPath)
if err != nil {
t.Fatalf("Chart.yaml file %s does not exist", chartPath)
}
testutil.CompareWithFixture(t, chartFileContent, testutil.WithSuffix("_chart"))

// check if the values.yaml file exists
valuesPath := tmpDir + "/values.yaml"
valuesFileContent, err := os.ReadFile(valuesPath)
if err != nil {
t.Fatalf("values.yaml file %s does not exist", valuesPath)
}
testutil.CompareWithFixture(t, valuesFileContent, testutil.WithSuffix("_values"))

}
Loading