Skip to content

Commit

Permalink
helm chart install option
Browse files Browse the repository at this point in the history
Introduce a install helm command to generate a helm chart with some templatization options similar to the install render --template command.

the PR also introduces a way to template the registry overrides option of the operator

required by Azure/ARO-HCP#698

Signed-off-by: Gerd Oberlechner <[email protected]>
  • Loading branch information
geoberle committed Oct 24, 2024
1 parent d29e8a1 commit f397009
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 61 deletions.
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
153 changes: 153 additions & 0 deletions cmd/install/install_helm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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",
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",
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", "", "File to write the rendered manifests to. Writes to STDOUT if not specified.")

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": "",
"registryOverrides": "",
"oidc": map[string]interface{}{
"s3": map[string]interface{}{
"name": "",
"region": "",
"credsSecret": "",
"credsSecretKey": "",
},
},
"aws": map[string]interface{}{
"private": map[string]interface{}{
"region": "",
"credsSecret": "",
"credsSecretKey": "",
},
},
"externaldns": map[string]interface{}{
"credsSecret": "",
"domainFilter": "",
"txtOwnerId": "",
"image": "",
},
}
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
}
69 changes: 69 additions & 0 deletions cmd/install/install_helm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package install

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

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"
if _, err := os.Stat(chartPath); os.IsNotExist(err) {
t.Fatalf("Chart.yaml file %s does not exist", chartPath)
}

// check if the values.yaml file exists
valuesPath := tmpDir + "/values.yaml"
if _, err := os.Stat(valuesPath); os.IsNotExist(err) {
t.Fatalf("values.yaml file %s does not exist", valuesPath)
}

}
Loading

0 comments on commit f397009

Please sign in to comment.