Skip to content

Commit

Permalink
build: Include Calico images in image list
Browse files Browse the repository at this point in the history
Calico images are deployed by the tigera-operator and as such are not
discoverable by the helm list images plugin. Using the extra images file
functionality of the helm list images plugin allows for adding these
images as templated strings that will adapt with the chart version as it
is updated in future.

This commit also fixes up some other things:

- Use `filepath` instead of `path` for portabiity
- Output the image list to stdout from make to allow user to decide
  where to redirect list to
- Omit changing dir from image list when running as goreleaser hook
- Slightly more defensive when checkinbg errors
  • Loading branch information
jimmidyson committed Sep 9, 2024
1 parent d5f53bd commit 0fe0c98
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ before:
- make template-helm-repository
- |
sh -ec 'if [ {{ .IsSnapshot }} == false ] ; then
env CAREN_VERSION=v{{ trimprefix .Version "v" }} make list-images
make --no-print-directory CAREN_VERSION=v{{ trimprefix .Version "v" }} list-images >caren-images.txt
fi'
builds:
Expand Down
132 changes: 94 additions & 38 deletions hack/tools/fetch-images/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"slices"
"strings"
"text/template"
Expand All @@ -20,6 +20,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/yaml"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)
Expand All @@ -30,10 +31,11 @@ const (
)

type ChartInfo struct {
repo string
name string
valuesFile string
stringValues []string
repo string
name string
valuesFile string
stringValues []string
extraImagesFiles []string
}

func main() {
Expand All @@ -47,9 +49,9 @@ func main() {
flagSet.StringVar(&chartDirectory, "chart-directory", "",
"path to chart directory for CAREN")
flagSet.StringVar(&helmChartConfigMap, "helm-chart-configmap", "",
"path to chart directory for CAREN")
"path to helm chart configmap for CAREN")
flagSet.StringVar(&carenVersion, "caren-version", "",
"caren version for images override")
"CAREN version for images override")
err := flagSet.Parse(args[1:])
if err != nil {
fmt.Println("failed to parse args", err.Error())
Expand All @@ -66,7 +68,7 @@ func main() {
os.Exit(1)
}
if chartDirectory == "" || helmChartConfigMap == "" {
fmt.Println("chart-directory helm-chart-configmap must be set")
fmt.Println("chart-directory and helm-chart-configmap must be set")
os.Exit(1)
}
i := &ChartInfo{
Expand Down Expand Up @@ -97,15 +99,10 @@ func main() {
}

func EnsureFullPath(filename string) (string, error) {
if path.IsAbs(filename) {
return filename, nil
}
wd, err := os.Getwd()
fullPath, err := filepath.Abs(filename)
if err != nil {
return "", fmt.Errorf("failed to get wd: %w", err)
return "", err
}
fullPath := path.Join(wd, filename)
fullPath = path.Clean(fullPath)
_, err = os.Stat(fullPath)
if err != nil {
return "", err
Expand All @@ -129,7 +126,7 @@ func getImagesForAddons(helmChartConfigMap, carenChartDirectory string) ([]strin
if err != nil {
return nil, fmt.Errorf("failed to unmarshal configmap to object %w", err)
}
images := []string{}
var images []string
for _, chartInfoRaw := range cm.Data {
var settings HelmChartFromConfigMap
err = yamlv2.Unmarshal([]byte(chartInfoRaw), &settings)
Expand All @@ -140,19 +137,24 @@ func getImagesForAddons(helmChartConfigMap, carenChartDirectory string) ([]strin
name: settings.ChartName,
repo: settings.Repository,
}
valuesFile := getValuesFileForChartIfNeeded(settings.ChartName, carenChartDirectory)
valuesFile, err := getValuesFileForChartIfNeeded(settings.ChartName, carenChartDirectory)
if err != nil {
return nil, fmt.Errorf("failed to get values file for %s: %w", settings.ChartName, err)
}
if valuesFile != "" {
info.valuesFile = valuesFile
}
if settings.ChartName == "aws-cloud-controller-manager" {

switch settings.ChartName {
case "aws-cloud-controller-manager":
values, err := getHelmValues(carenChartDirectory)
if err != nil {
return nil, err
}
awsImages, found, err := unstructured.NestedStringMap(values, "hooks", "ccm", "aws", "k8sMinorVersionToCCMVersion")
if !found {
return images, fmt.Errorf("failed to find k8sMinorVersionToCCMVersion from file %s",
path.Join(carenChartDirectory, "values.yaml"))
filepath.Join(carenChartDirectory, "values.yaml"))
}
if err != nil {
return images, fmt.Errorf("failed to get map k8sMinorVersionToCCMVersion with error %w",
Expand All @@ -168,9 +170,33 @@ func getImagesForAddons(helmChartConfigMap, carenChartDirectory string) ([]strin
}
images = append(images, chartImages...)
}
// skip the to next addon because we got what we needed
// skip to the next addon because we got what we needed
continue
case "tigera-operator":
extraImagesFile, err := os.CreateTemp("", "")
if err != nil {
return nil, fmt.Errorf("failed to create temp file for extra Calico images: %w", err)
}
defer os.Remove(extraImagesFile.Name()) //nolint:gocritic // Won't be leaked.
_, err = extraImagesFile.WriteString(`
{{default "docker.io/" .Values.installation.registry }}calico/cni:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/kube-controllers:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/node:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/apiserver:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/pod2daemon-flexvol:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/typha:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/csi:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/node-driver-registrar:{{ .Chart.Version }}
{{default "docker.io/" .Values.installation.registry }}calico/ctl:{{ .Chart.Version }}
`)
_ = extraImagesFile.Close()
if err != nil {
return nil, fmt.Errorf("failed to write to temp file for extra Calico images: %w", err)
}

info.extraImagesFiles = append(info.extraImagesFiles, extraImagesFile.Name())
}

chartImages, err := getImagesForChart(info)
if err != nil {
return nil, fmt.Errorf("failed to get images for %s with error %w", info.name, err)
Expand All @@ -181,7 +207,7 @@ func getImagesForAddons(helmChartConfigMap, carenChartDirectory string) ([]strin
}

func getHelmValues(carenChartDirectory string) (map[string]interface{}, error) {
values := path.Join(carenChartDirectory, "values.yaml")
values := filepath.Join(carenChartDirectory, "values.yaml")
valuesFile, err := os.Open(values)
if err != nil {
return nil, fmt.Errorf("failed to open file %s with %w", values, err)
Expand All @@ -195,21 +221,50 @@ func getHelmValues(carenChartDirectory string) (map[string]interface{}, error) {
return m, nil
}

func getValuesFileForChartIfNeeded(chartName, carenChartDirectory string) string {
func getValuesFileForChartIfNeeded(chartName, carenChartDirectory string) (string, error) {
switch chartName {
case "nutanix-csi-storage":
return path.Join(carenChartDirectory, "addons", "csi", "nutanix", defaultHelmAddonFilename)
return filepath.Join(carenChartDirectory, "addons", "csi", "nutanix", defaultHelmAddonFilename), nil
case "node-feature-discovery":
return path.Join(carenChartDirectory, "addons", "nfd", defaultHelmAddonFilename)
return filepath.Join(carenChartDirectory, "addons", "nfd", defaultHelmAddonFilename), nil
case "snapshot-controller":
return path.Join(carenChartDirectory, "addons", "csi", "snapshot-controller", defaultHelmAddonFilename)
return filepath.Join(carenChartDirectory, "addons", "csi", "snapshot-controller", defaultHelmAddonFilename), nil
case "cilium":
return path.Join(carenChartDirectory, "addons", "cni", "cilium", defaultHelmAddonFilename)
return filepath.Join(carenChartDirectory, "addons", "cni", "cilium", defaultHelmAddonFilename), nil
// Calico values differ slightly per provider, but that does not have a material imapct on the images required
// so we can use the default values file for AWS provider.
case "tigera-operator":
f := filepath.Join(carenChartDirectory, "addons", "cni", "calico", "aws", defaultHelmAddonFilename)
tempFile, err := os.CreateTemp("", "")
if err != nil {
return "", fmt.Errorf("failed to create temp file: %w", err)
}

// CAAPH uses unstructured internally, so we need to create an unstructured copy of a cluster
// to pass to the CAAPH values template.
c, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&clusterv1.Cluster{})

templateInput := struct {
Cluster map[string]interface{}
}{
Cluster: c,
}

err = template.Must(template.New(defaultHelmAddonFilename).ParseFiles(f)).Execute(tempFile, &templateInput)
if err != nil {
return "", fmt.Errorf("failed to execute helm values template %w", err)
}

return tempFile.Name(), nil
// this uses the values from kustomize because the file at addons/cluster-autoscaler/values-template.yaml
// is a file that is templated
case "cluster-autoscaler":
f := path.Join(carenChartDirectory, "addons", "cluster-autoscaler", defaultHelmAddonFilename)
tempFile, _ := os.CreateTemp("", "")
f := filepath.Join(carenChartDirectory, "addons", "cluster-autoscaler", defaultHelmAddonFilename)
tempFile, err := os.CreateTemp("", "")
if err != nil {
return "", fmt.Errorf("failed to create temp file: %w", err)
}

c := clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "tmplCluster",
Expand All @@ -227,25 +282,26 @@ func getValuesFileForChartIfNeeded(chartName, carenChartDirectory string) string
Cluster: &c,
}

template.Must(template.New(defaultHelmAddonFilename).ParseFiles(f)).Execute(tempFile, &templateInput)
return tempFile.Name()
err = template.Must(template.New(defaultHelmAddonFilename).ParseFiles(f)).Execute(tempFile, &templateInput)
if err != nil {
return "", fmt.Errorf("failed to execute helm values template %w", err)
}

return tempFile.Name(), nil
default:
return ""
return "", nil
}
}

func getImagesForChart(info *ChartInfo) ([]string, error) {
images := pkg.Images{}
images.SetChart(info.name)
if info.repo != "" {
images.RepoURL = info.repo
}
images.RepoURL = info.repo
if info.valuesFile != "" {
images.ValueFiles.Set(info.valuesFile)
}
if len(info.stringValues) > 0 {
images.StringValues = info.stringValues
_ = images.ValueFiles.Set(info.valuesFile)
}
images.StringValues = info.stringValues
images.ExtraImagesFiles = info.extraImagesFiles
// kubeVersion needs to be set for some addons
images.KubeVersion = "v1.29.0"
images.SetRelease("sample")
Expand Down
9 changes: 4 additions & 5 deletions make/addons.mk
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ template-helm-repository: generate-mindthegap-repofile ## this is used by goreal

.PHONY: list-images
list-images:
cd hack/tools/fetch-images && go build
./hack/tools/fetch-images/fetch-images \
-chart-directory=./charts/cluster-api-runtime-extensions-nutanix/ \
-helm-chart-configmap=./charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml \
-caren-version=$(CAREN_VERSION) >> caren-images.txt
cd hack/tools/fetch-images && go run . \
-chart-directory=$(PWD)/charts/cluster-api-runtime-extensions-nutanix/ \
-helm-chart-configmap=$(PWD)/charts/cluster-api-runtime-extensions-nutanix/templates/helm-config.yaml \
-caren-version=$(CAREN_VERSION)

0 comments on commit 0fe0c98

Please sign in to comment.