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

Include v1beta2 Helm charts images to known images in air gapped mode #4458

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 32 additions & 31 deletions pkg/apparchive/helm-v1beta2.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Source: my-chart/templates/my-chart-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-chart-configmap
data: {}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -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