Skip to content

Commit

Permalink
Include v1beta2 Helm charts images to known images in air gapped mode (
Browse files Browse the repository at this point in the history
…#4458)

* Include v1beta2 Helm charts images to known images in air gapped mode
  • Loading branch information
sgalsaleh committed Feb 19, 2024
1 parent 86e0f97 commit 6e3b31d
Show file tree
Hide file tree
Showing 81 changed files with 1,830 additions and 31 deletions.
63 changes: 32 additions & 31 deletions pkg/apparchive/helm-v1beta2.go
Expand Up @@ -146,6 +146,27 @@ func WriteV1Beta2HelmCharts(opts WriteV1Beta2HelmChartsOptions) error {
return errors.Wrap(err, "failed to write values file")
}

chartImages, err := findV1Beta2HelmChartImages(opts, &helmChart, chartDir)
if err != nil {
return errors.Wrap(err, "failed to find chart images")
}

var dockerHubRegistryCreds registry.Credentials
dockerhubSecret, _ := registry.GetDockerHubPullSecret(opts.Clientset, util.PodNamespace, opts.ProcessImageOptions.Namespace, opts.ProcessImageOptions.AppSlug)
if dockerhubSecret != nil {
dockerHubRegistryCreds, _ = registry.GetCredentialsForRegistryFromConfigJSON(dockerhubSecret.Data[".dockerconfigjson"], registry.DockerHubRegistryName)
}

if err := image.UpdateInstallationImages(image.UpdateInstallationImagesOptions{
Images: chartImages,
KotsKinds: opts.KotsKinds,
IsAirgap: opts.ProcessImageOptions.IsAirgap,
UpstreamDir: opts.Upstream.GetUpstreamDir(opts.WriteUpstreamOptions),
DockerHubRegistryCreds: dockerHubRegistryCreds,
}); err != nil {
return errors.Wrap(err, "failed to update installation images")
}

if !opts.ProcessImageOptions.RewriteImages || opts.ProcessImageOptions.IsAirgap {
// if an on-prem registry is not configured (which means it's an online installation)
// there's no need to process/copy the images as they will be pulled from their original registries or through the replicated proxy.
Expand All @@ -154,8 +175,8 @@ func WriteV1Beta2HelmCharts(opts WriteV1Beta2HelmChartsOptions) error {
continue
}

if err := processOnlineV1Beta2HelmChartImages(opts, &helmChart, chartDir); err != nil {
return errors.Wrap(err, "failed to process online images")
if err := image.CopyOnlineImages(opts.ProcessImageOptions, chartImages, opts.KotsKinds, opts.KotsKinds.License, dockerHubRegistryCreds, opts.RenderOptions.Log); err != nil {
return errors.Wrap(err, "failed to copy online images")
}
}

Expand Down Expand Up @@ -264,62 +285,42 @@ func templateV1Beta2HelmChartWithValuesToDir(helmChart *kotsv1beta2.HelmChart, c
return nil
}

func processOnlineV1Beta2HelmChartImages(opts WriteV1Beta2HelmChartsOptions, helmChart *kotsv1beta2.HelmChart, chartDir string) error {
func findV1Beta2HelmChartImages(opts WriteV1Beta2HelmChartsOptions, helmChart *kotsv1beta2.HelmChart, chartDir string) ([]string, error) {
// template the chart with the builder values to a temp dir and then process images
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("kots-images-%s", helmChart.GetDirName()))
if err != nil {
return errors.Wrap(err, "failed to create temp dir for image processing")
return nil, errors.Wrap(err, "failed to create temp dir for image processing")
}
defer os.RemoveAll(tmpDir)

builderHelmValues, err := helmChart.GetBuilderValues()
if err != nil {
return errors.Wrap(err, "failed to get builder values for chart")
return nil, errors.Wrap(err, "failed to get builder values for chart")
}

builderValuesContent, err := yaml.Marshal(builderHelmValues)
if err != nil {
return errors.Wrap(err, "failed to marshal builder values")
return nil, errors.Wrap(err, "failed to marshal builder values")
}

builderValuesPath := path.Join(tmpDir, "builder-values.yaml")
if err := os.WriteFile(builderValuesPath, builderValuesContent, 0644); err != nil {
return errors.Wrap(err, "failed to write builder values file")
return nil, errors.Wrap(err, "failed to write builder values file")
}

templatedOutputDir := path.Join(tmpDir, helmChart.GetDirName())
if err := os.Mkdir(templatedOutputDir, 0755); err != nil {
return errors.Wrap(err, "failed to create temp dir for image processing")
return nil, errors.Wrap(err, "failed to create temp dir for image processing")
}

if err := templateV1Beta2HelmChartWithValuesToDir(helmChart, chartDir, builderValuesPath, templatedOutputDir, opts.RenderOptions.Log.Debug); err != nil {
return errors.Wrap(err, "failed to template helm chart for image processing")
}

var dockerHubRegistryCreds registry.Credentials
dockerhubSecret, _ := registry.GetDockerHubPullSecret(opts.Clientset, util.PodNamespace, opts.ProcessImageOptions.Namespace, opts.ProcessImageOptions.AppSlug)
if dockerhubSecret != nil {
dockerHubRegistryCreds, _ = registry.GetCredentialsForRegistryFromConfigJSON(dockerhubSecret.Data[".dockerconfigjson"], registry.DockerHubRegistryName)
return nil, errors.Wrap(err, "failed to template helm chart for image processing")
}

chartImages, err := image.FindImagesInDir(templatedOutputDir)
if err != nil {
return errors.Wrap(err, "failed to find base images")
}

if err := image.UpdateInstallationImages(image.UpdateInstallationImagesOptions{
Images: chartImages,
KotsKinds: opts.KotsKinds,
IsAirgap: opts.ProcessImageOptions.IsAirgap,
UpstreamDir: opts.Upstream.GetUpstreamDir(opts.WriteUpstreamOptions),
DockerHubRegistryCreds: dockerHubRegistryCreds,
}); err != nil {
return errors.Wrap(err, "failed to update installation images")
return nil, errors.Wrap(err, "failed to find images in dir")
}

if err := image.CopyOnlineImages(opts.ProcessImageOptions, chartImages, opts.KotsKinds, opts.KotsKinds.License, dockerHubRegistryCreds, opts.RenderOptions.Log); err != nil {
return errors.Wrap(err, "failed to rewrite base images")
}

return nil
return chartImages, nil
}
18 changes: 18 additions & 0 deletions pkg/tests/pull/cases/airgap/testcase.yaml
@@ -0,0 +1,18 @@
Name: test airgap
PullOptions:
Namespace: app-namespace
ExcludeAdminConsole: true
IsAirgap: true
Silent: true
LocalPath: cases/airgap/upstream
RootDir: cases/airgap/results
SharedPassword: dummy-pass
RewriteImages: true
RewriteImageOptions:
Hostname: ttl.sh
Namespace: test
Username: test
Password: fake-pass
IsReadOnly: true
Downstreams:
- this-cluster
8 changes: 8 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/kots-app.yaml
@@ -0,0 +1,8 @@
apiVersion: kots.io/v1beta1
kind: Application
metadata:
name: my-app
annotations:
kots.io/exclude: "true"
spec:
title: my-app
Binary file not shown.
19 changes: 19 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/my-chart.yaml
@@ -0,0 +1,19 @@
apiVersion: kots.io/v1beta1
kind: HelmChart
metadata:
name: my-chart
spec:
# chart identifies a matching chart from a .tgz
chart:
name: my-chart
chartVersion: 0.1.0
releaseName: my-chart-release
useHelmInstall: true

# values are used in the customer environment, as a pre-render step
# these values will be supplied to helm template
values: {}

# builder values provide a way to render the chart with all images
# and manifests. this is used in replicated to create airgap packages
builder: {}
Binary file not shown.
18 changes: 18 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/replicated.yaml
@@ -0,0 +1,18 @@
apiVersion: kots.io/v1beta2
kind: HelmChart
metadata:
name: replicated
annotations:
kots.io/exclude: "true"
spec:
chart:
name: replicated
chartVersion: 1.0.0-beta.12
releaseName: replicated-release
values:
my-value: my-value
optionalValues:
- when: 'repl{{ "true" }}'
recursiveMerge: true
values:
my-optional-value: my-optional-value
25 changes: 25 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/subdir/configmap.yaml
@@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: test-licenseinfo
data:
dockerCfg: {{repl LicenseDockerCfg }}
licenseFieldValueIsGitOpsSupported: {{repl LicenseFieldValue "isGitOpsSupported" }}
licenseFieldValueIsIdentityServiceSupported: {{repl LicenseFieldValue "isIdentityServiceSupported" }}
licenseFieldValueIsGeoaxisSupported: {{repl LicenseFieldValue "isGeoaxisSupported" }}
licenseFieldValueIsAirgapSupported: {{repl LicenseFieldValue "isAirgapSupported" }}
licenseFieldValueLicenseType: {{repl LicenseFieldValue "licenseType" }}
licenseFieldValueLicenseSequence: {{repl LicenseFieldValue "licenseSequence" }}
licenseFieldValueSignature: '{{repl LicenseFieldValue "signature" }}'
licenseFieldValueAppSlug: {{repl LicenseFieldValue "appSlug" }}
licenseFieldValueChannelID: {{repl LicenseFieldValue "channelID" }}
licenseFieldValueChannelName: {{repl LicenseFieldValue "channelName" }}
licenseFieldValueCustomerName: {{repl LicenseFieldValue "customerName" }}
licenseFieldValueEndpoint: {{repl LicenseFieldValue "endpoint" }}
licenseFieldValueLicenseID: {{repl LicenseFieldValue "licenseID" }}
licenseFieldValueLicenseId: {{repl LicenseFieldValue "licenseId" }}
licenseFieldValueCustomIntField: {{repl LicenseFieldValue "int_field" }}
licenseFieldValueCustomStringField: {{repl LicenseFieldValue "string_field" }}
licenseFieldValueCustomTextField: "{{repl LicenseFieldValue "text_field" | replace "\n" "\\n" }}"
licenseFieldValueCustomBoolField: "{{repl LicenseFieldValue "bool_field" }}"
licenseFieldValueCustomHiddenField: "{{repl LicenseFieldValue "hidden_field" }}"
34 changes: 34 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/subdir/nginx.yaml
@@ -0,0 +1,34 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx
---
kind: Service
apiVersion: v1
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: nginx
containers:
- name: nginx
image: nginx:1.24
Binary file not shown.
19 changes: 19 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/test-chart.yaml
@@ -0,0 +1,19 @@
apiVersion: kots.io/v1beta1
kind: HelmChart
metadata:
name: test-chart
spec:
# chart identifies a matching chart from a .tgz
chart:
name: test-chart
chartVersion: 0.1.0
useHelmInstall: false
namespace: helmns

# values are used in the customer environment, as a pre-render step
# these values will be supplied to helm template
values: {}

# builder values provide a way to render the chart with all images
# and manifests. this is used in replicated to create airgap packages
builder: {}
10 changes: 10 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/userdata/config.yaml
@@ -0,0 +1,10 @@
apiVersion: kots.io/v1beta1
kind: ConfigValues
metadata:
creationTimestamp: null
name: my-app
spec:
values:
hostname:
value: "my-app.somebigbank.com"
status: {}
47 changes: 47 additions & 0 deletions pkg/tests/pull/cases/airgap/upstream/userdata/license.yaml
@@ -0,0 +1,47 @@
apiVersion: kots.io/v1beta1
kind: License
metadata:
name: testcustomer
spec:
appSlug: my-app
channelID: 1vusIYZLAVxMG6q760OJmRKj5i5
channelName: My Channel
customerName: Test Customer
endpoint: https://replicated.app
entitlements:
bool_field:
title: Bool Field
value: true
valueType: Boolean
expires_at:
description: License Expiration
title: Expiration
value: "2030-07-27T00:00:00Z"
valueType: String
hidden_field:
isHidden: true
title: Hidden Field
value: this is secret
valueType: String
int_field:
title: Int Field
value: 123
valueType: Integer
string_field:
title: StringField
value: single line text
valueType: String
text_field:
title: Text Field
value: |-
multi
line
text
valueType: Text
isAirgapSupported: true
isGitOpsSupported: true
isSnapshotSupported: true
licenseID: 1vusOokxAVp1tkRGuyxnF23PJcq
licenseSequence: 7
licenseType: prod
signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pZEdWemRHTjFjM1J2YldWeUluMHNJbk53WldNaU9uc2liR2xqWlc1elpVbEVJam9pTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54SWl3aWJHbGpaVzV6WlZSNWNHVWlPaUp3Y205a0lpd2lZM1Z6ZEc5dFpYSk9ZVzFsSWpvaVZHVnpkQ0JEZFhOMGIyMWxjaUlzSW1Gd2NGTnNkV2NpT2lKdGVTMWhjSEFpTENKamFHRnVibVZzU1VRaU9pSXhkblZ6U1ZsYVRFRldlRTFITm5FM05qQlBTbTFTUzJvMWFUVWlMQ0pqYUdGdWJtVnNUbUZ0WlNJNklrMTVJRU5vWVc1dVpXd2lMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPamNzSW1WdVpIQnZhVzUwSWpvaWFIUjBjSE02THk5eVpYQnNhV05oZEdWa0xtRndjQ0lzSW1WdWRHbDBiR1Z0Wlc1MGN5STZleUppYjI5c1gyWnBaV3hrSWpwN0luUnBkR3hsSWpvaVFtOXZiQ0JHYVdWc1pDSXNJblpoYkhWbElqcDBjblZsTENKMllXeDFaVlI1Y0dVaU9pSkNiMjlzWldGdUluMHNJbVY0Y0dseVpYTmZZWFFpT25zaWRHbDBiR1VpT2lKRmVIQnBjbUYwYVc5dUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTWFXTmxibk5sSUVWNGNHbHlZWFJwYjI0aUxDSjJZV3gxWlNJNklqSXdNekF0TURjdE1qZFVNREE2TURBNk1EQmFJaXdpZG1Gc2RXVlVlWEJsSWpvaVUzUnlhVzVuSW4wc0ltaHBaR1JsYmw5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtocFpHUmxiaUJHYVdWc1pDSXNJblpoYkhWbElqb2lkR2hwY3lCcGN5QnpaV055WlhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lMQ0pwYzBocFpHUmxiaUk2ZEhKMVpYMHNJbWx1ZEY5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtsdWRDQkdhV1ZzWkNJc0luWmhiSFZsSWpveE1qTXNJblpoYkhWbFZIbHdaU0k2SWtsdWRHVm5aWElpZlN3aWMzUnlhVzVuWDJacFpXeGtJanA3SW5ScGRHeGxJam9pVTNSeWFXNW5SbWxsYkdRaUxDSjJZV3gxWlNJNkluTnBibWRzWlNCc2FXNWxJSFJsZUhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lmU3dpZEdWNGRGOW1hV1ZzWkNJNmV5SjBhWFJzWlNJNklsUmxlSFFnUm1sbGJHUWlMQ0oyWVd4MVpTSTZJbTExYkhScFhHNXNhVzVsWEc1MFpYaDBJaXdpZG1Gc2RXVlVlWEJsSWpvaVZHVjRkQ0o5ZlN3aWFYTkJhWEpuWVhCVGRYQndiM0owWldRaU9uUnlkV1VzSW1selIybDBUM0J6VTNWd2NHOXlkR1ZrSWpwMGNuVmxMQ0pwYzFOdVlYQnphRzkwVTNWd2NHOXlkR1ZrSWpwMGNuVmxmWDA9IiwiaW5uZXJTaWduYXR1cmUiOiJleUpzYVdObGJuTmxVMmxuYm1GMGRYSmxJam9pYUhneE1XTXZUR1ozUTNoVE5YRmtRWEJGU1hGdVRrMU9NMHBLYTJzNFZHZFhSVVpzVDFKVlJ6UjJjR1YzZEZoV1YzbG1lamRZY0hBd1ExazJZamRyUVRSS2N6TklhR3d3YkZJMFdUQTFMemN2UVVkQ2FEZFZNSGczUkhaTVozUXpVM00wYm5GTFZTdFhXRXBTVHpKWVFVRnZSME4xZFRWR1RGcHJRVWhYY1RSUVFtMXphSFY2Y1ZsdmNucHhlbGhGWVZWVlpFUlVkVXhDTW1nNWFIZ3dXRWhQUmxwUk16bHVkbTlPUjJaT2R5OTRTVmRaZEhSUGRYZHZhMncyTVZsb1JVeFZlRmQxU1ZSRmMwTlVhM2xtTVRNd09IazVSbFJzWlRKeVYyZEVlSEZNYTBSUFNXVXlPRWwzUzJSQkwySXdWVUl5VEZGbVRWcHdWemwyUTNCSkwybHlWek5uYmpaeU5WWjNWMjB2U1dweWJtNDNSelJrVmpadVYzcFRkMGhQUTJSdWEwMTRNRXQ1VVVOa0wxQjFaWEpUYjNSdVEwOXRTMDEzWlRSTGJqaERkMU5YVVRRNGRURkRNbTFpV1VzeGRYTlpOM1YzUFQwaUxDSndkV0pzYVdOTFpYa2lPaUl0TFMwdExVSkZSMGxPSUZCVlFreEpReUJMUlZrdExTMHRMVnh1VFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUZ6TkhKdlVIcDFhV1JNZVhOMmIxWTJkemxhTkZ4dVdHRmliME5tWTJNeGFHZFZhQ3N3V1VkS2NFNURSVXhyTjBaTFF5OTJhemR6ZERsR05tY3dUMjlrU0VSbGVYZFJXa2hLZFU1TVpsUnNRbEJHUTJOaU5seHVObTlzVEZOeWNGQTRjbFUzU0d4SGJsRkVSMFJNYVhkS1EyaGtSRGRVVUdSM2FXdHBkMHRGY201aldqaEdaalZsU25vd2RETmlUWFpyVDJaVVluSkJiRnh1WWtGQ1kwbzVNVmxVT1hKdVVXOXFkVWN4UldKUVRqaEZWblI2TWxZNE5IZHViR2Q0TUhCd2JEVjRPSFpOYlhwcE1ISnVibEZVV1VGamJ6WnFhMnBJTTF4dVRuTlVkWE4xUzFkdlJGUjVNWE5yZGtSUk9IbEJZV0ptWTNNME4zWnNRazAwU0RGT1JFNHZSSFJhWWxZdllubDJia0o2YkM4eFZrVnpURmRqWlZWcFRGeHVSWEYxT0VkeWF5dFFVRGQyUkdSd2JFUjNjWFpQV2t4RmRYazNkamhuUm01U09WUlVSV3ByTlVvNWRuWlVTR2RtU25VemVubEVPR2xLWTBSRE5YcHFPVnh1YjFGSlJFRlJRVUpjYmkwdExTMHRSVTVFSUZCVlFreEpReUJMUlZrdExTMHRMVnh1SWl3aWEyVjVVMmxuYm1GMGRYSmxJam9pWlhsS2VtRlhaSFZaV0ZJeFkyMVZhVTlwU2pCUldIQjJXVE5LVms1NmFGaFNSMlJzVVRKb2NtTklXa1ZVVlRsRldqQktXVTFGUmtaVFJFNUZVMGhLYkUxclRUTkxNSEJFVkROR2VGTnROVVJVVlRWVlltMDFiVnBGUm5sWldIQjZaRVJqTVZaSGFFeFBXRUpVVWtacmRrd3diek5aTUZaSlVteFdWRXd5T1VoV1JXeHNWa1ZPTUZSSE1WWlJNR04zVkd4R2JGa3pTblJUUm1zMFZVWk9hMVpWU2pCVU1WbDNZbXQwY0ZSclZuQmpia0poVFZjNWFtSldiSEZaYTNob1UyeHNWV0pGUmtWWGJVWnZWakZLVUZkcWJGSmhXRVp1V2xkb1EyRnVRak5TUjNNd1lWWkpOVTVXVmxkV1ZUVnlUMGhLYjFsVlRYbGhiVGcwVjBkYWVGbHFWbFppYlhoeFpFWkZkMDU1Y3pCaFZsSkpWRVpPTm1WRk1IcGxWWFJ2VFVaR1ZtRXdWVFJSVnpsSFVsaEtVRTFZUmxCU01WcFJVMVJDTmxsV2FIcFdWWEJ0WTBSU2JFMVVRazlPVjNSU1ZucFdUMU5XWTNaU1ZYUkZVMGhzYlU5VmJGaGtNMUl3WTFWc1lXTlhSakJTYTA1RVlVWmtjbUo2VmtSU00wSllUREkxUmsxWVl6SmxWM1JKVlZoQk1sVXhTbEppU0Zwd1VrVXdNRlpFVWt0VU1rWnNVVmQwYzFSV1VrMVVWV055V1RCYVRHSXpaRTlUVm05NVlraE9SR1JzVG5aUmFrWmFaVmRPVGxOVlNteGFiRXB1Wld0U2RVMHhSVGxRVTBselNXMWtjMkl5U21oaVJYUnNaVlZzYTBscWIybFpiVkpzV2xSVk1rNVVXWGRaTWxwcFRrUk9hazlYU1hsUFIwcHRUMVJvYkZsWFRtaGFiVVV5VGtSWmFXWlJQVDBpZlE9PSJ9
@@ -0,0 +1,6 @@
apiVersion: v2
appVersion: 0.1.0
description: A Helm chart for Kubernetes
name: my-chart
type: application
version: 0.1.0
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
metadata:
annotations:
kots.io/kustomization: base
resources:
- templates/my-chart-configmap.yaml
- templates/my-chart-pod.yaml
@@ -0,0 +1,6 @@
# Source: my-chart/templates/my-chart-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-chart-configmap
data: {}
@@ -0,0 +1,11 @@
# Source: my-chart/templates/my-chart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-chart-pod
spec:
containers:
- name: my-chart-container
image: nginx:1.23
ports:
- containerPort: 80
@@ -0,0 +1 @@
{}
@@ -0,0 +1,6 @@
apiVersion: v2
appVersion: 1.16.0
description: A Helm chart for Kubernetes
name: test-chart
type: application
version: 0.1.0

0 comments on commit 6e3b31d

Please sign in to comment.