Skip to content

Commit 3c24246

Browse files
author
Craig O'Donnell
authored
pass embedded cluster artifacts in upstream upgrade (#4549)
1 parent bebc3e9 commit 3c24246

File tree

23 files changed

+309
-126
lines changed

23 files changed

+309
-126
lines changed

cmd/kots/cli/admin-console-push-images.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func AdminPushImagesCmd() *cobra.Command {
5454
}
5555

5656
if _, err := os.Stat(imageSource); err == nil {
57-
_, err = image.TagAndPushImagesFromBundle(imageSource, *options)
57+
err = image.TagAndPushImagesFromBundle(imageSource, *options)
5858
if err != nil {
5959
return errors.Wrap(err, "failed to push images")
6060
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ require (
4949
github.com/pkg/errors v0.9.1
5050
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
5151
github.com/replicatedhq/embedded-cluster-kinds v1.1.2
52-
github.com/replicatedhq/kotskinds v0.0.0-20240326213823-6a0ed11e7397
52+
github.com/replicatedhq/kotskinds v0.0.0-20240416132840-4e646b87f7a1
5353
github.com/replicatedhq/kurlkinds v1.5.0
5454
github.com/replicatedhq/troubleshoot v0.87.0
5555
github.com/replicatedhq/yaml/v3 v3.0.0-beta5-replicatedhq

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,12 @@ github.com/replicatedhq/embedded-cluster-kinds v1.1.2 h1:2ITzcUzh5uh0fsnfZsVHvkw
13101310
github.com/replicatedhq/embedded-cluster-kinds v1.1.2/go.mod h1:LheSDOgMngMRAbwAj0sVZUVv2ciKIVR2bYTMeOBGwlg=
13111311
github.com/replicatedhq/kotskinds v0.0.0-20240326213823-6a0ed11e7397 h1:JNuBcFH9D3Osyi+1QUwdvaAklEd6HXznqZDfpWlr73M=
13121312
github.com/replicatedhq/kotskinds v0.0.0-20240326213823-6a0ed11e7397/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
1313+
github.com/replicatedhq/kotskinds v0.0.0-20240402213802-a6d75e97be70 h1:55fMr60YysSf+ac5zeW+xTIIJ8edUpAjorQyFn0iP2c=
1314+
github.com/replicatedhq/kotskinds v0.0.0-20240402213802-a6d75e97be70/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
1315+
github.com/replicatedhq/kotskinds v0.0.0-20240415152931-2837245679f5 h1:SI1Ar5c6QWTm1IpPOO/CVv8dktdqd8KaQUEOeHEbhgM=
1316+
github.com/replicatedhq/kotskinds v0.0.0-20240415152931-2837245679f5/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
1317+
github.com/replicatedhq/kotskinds v0.0.0-20240416132840-4e646b87f7a1 h1:+RvMZ646tQTRzWFZTy6mnmgWJZOLFu6B9PXv8tcIcFY=
1318+
github.com/replicatedhq/kotskinds v0.0.0-20240416132840-4e646b87f7a1/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
13131319
github.com/replicatedhq/kurlkinds v1.5.0 h1:zZ0PKNeh4kXvSzVGkn62DKTo314GxhXg1TSB3azURMc=
13141320
github.com/replicatedhq/kurlkinds v1.5.0/go.mod h1:rUpBMdC81IhmJNCWMU/uRsMETv9P0xFoMvdSP/TAr5A=
13151321
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851 h1:eRlNDHxGfVkPCRXbA4BfQJvt5DHjFiTtWy3R/t4djyY=

pkg/embeddedcluster/util.go

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8-
"regexp"
98
"sort"
109
"time"
1110

1211
embeddedclusterv1beta1 "github.com/replicatedhq/embedded-cluster-kinds/apis/v1beta1"
1312
"github.com/replicatedhq/kots/pkg/k8sutil"
14-
"github.com/replicatedhq/kots/pkg/logger"
1513
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1614
corev1 "k8s.io/api/core/v1"
1715
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -26,13 +24,6 @@ const configMapNamespace = "embedded-cluster"
2624
// ErrNoInstallations is returned when no installation object is found in the cluster.
2725
var ErrNoInstallations = fmt.Errorf("no installations found")
2826

29-
var (
30-
chartsArtifactRegex = regexp.MustCompile(`\/embedded-cluster\/(charts\.tar\.gz):`)
31-
imagesArtifactRegex = regexp.MustCompile(`\/embedded-cluster\/(images-.+\.tar):`)
32-
binaryArtifactRegex = regexp.MustCompile(`\/embedded-cluster\/(embedded-cluster-.+):`)
33-
metadataArtifactRegex = regexp.MustCompile(`\/embedded-cluster\/(version-metadata\.json):`)
34-
)
35-
3627
// ReadConfigMap will read the Kurl config from a configmap
3728
func ReadConfigMap(client kubernetes.Interface) (*corev1.ConfigMap, error) {
3829
return client.CoreV1().ConfigMaps(configMapNamespace).Get(context.TODO(), configMapName, metav1.GetOptions{})
@@ -113,27 +104,16 @@ func ClusterConfig(ctx context.Context) (*embeddedclusterv1beta1.ConfigSpec, err
113104
}
114105

115106
func getArtifactsFromInstallation(installation kotsv1beta1.Installation, appSlug string) *embeddedclusterv1beta1.ArtifactsLocation {
116-
if len(installation.Spec.EmbeddedClusterArtifacts) == 0 {
107+
if installation.Spec.EmbeddedClusterArtifacts == nil {
117108
return nil
118109
}
119110

120-
artifacts := &embeddedclusterv1beta1.ArtifactsLocation{}
121-
for _, artifact := range installation.Spec.EmbeddedClusterArtifacts {
122-
switch {
123-
case chartsArtifactRegex.MatchString(artifact):
124-
artifacts.HelmCharts = artifact
125-
case imagesArtifactRegex.MatchString(artifact):
126-
artifacts.Images = artifact
127-
case binaryArtifactRegex.MatchString(artifact):
128-
artifacts.EmbeddedClusterBinary = artifact
129-
case metadataArtifactRegex.MatchString(artifact):
130-
artifacts.EmbeddedClusterMetadata = artifact
131-
default:
132-
logger.Warnf("unknown artifact in installation: %s", artifact)
133-
}
111+
return &embeddedclusterv1beta1.ArtifactsLocation{
112+
EmbeddedClusterBinary: installation.Spec.EmbeddedClusterArtifacts.BinaryAmd64,
113+
Images: installation.Spec.EmbeddedClusterArtifacts.ImagesAmd64,
114+
HelmCharts: installation.Spec.EmbeddedClusterArtifacts.Charts,
115+
EmbeddedClusterMetadata: installation.Spec.EmbeddedClusterArtifacts.Metadata,
134116
}
135-
136-
return artifacts
137117
}
138118

139119
// startClusterUpgrade will create a new installation with the provided config.

pkg/embeddedcluster/util_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ func Test_getArtifactsFromInstallation(t *testing.T) {
3131
args: args{
3232
installation: kotsv1beta1.Installation{
3333
Spec: kotsv1beta1.InstallationSpec{
34-
EmbeddedClusterArtifacts: []string{
35-
"onprem.registry.com/my-app/embedded-cluster/charts.tar.gz:v1",
36-
"onprem.registry.com/my-app/embedded-cluster/images-amd64.tar:v1",
37-
"onprem.registry.com/my-app/embedded-cluster/embedded-cluster-amd64:v1",
38-
"onprem.registry.com/my-app/embedded-cluster/version-metadata.json:v1",
34+
EmbeddedClusterArtifacts: &kotsv1beta1.EmbeddedClusterArtifacts{
35+
Charts: "onprem.registry.com/my-app/embedded-cluster/charts.tar.gz:v1",
36+
ImagesAmd64: "onprem.registry.com/my-app/embedded-cluster/images-amd64.tar:v1",
37+
BinaryAmd64: "onprem.registry.com/my-app/embedded-cluster/embedded-cluster-amd64:v1",
38+
Metadata: "onprem.registry.com/my-app/embedded-cluster/version-metadata.json:v1",
3939
},
4040
},
4141
},

pkg/image/airgap.go

Lines changed: 56 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/replicatedhq/kots/pkg/imageutil"
2828
"github.com/replicatedhq/kots/pkg/kotsutil"
2929
"github.com/replicatedhq/kots/pkg/logger"
30+
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
3031
oras "oras.land/oras-go/v2"
3132
orasfile "oras.land/oras-go/v2/content/file"
3233
orasremote "oras.land/oras-go/v2/registry/remote"
@@ -108,9 +109,9 @@ func WriteProgressLine(progressWriter io.Writer, line string) {
108109
}
109110

110111
// CopyAirgapImages pushes images found in the airgap bundle/airgap root to the configured registry.
111-
func CopyAirgapImages(opts imagetypes.ProcessImageOptions, log *logger.CLILogger) (*imagetypes.CopyAirgapImagesResult, error) {
112+
func CopyAirgapImages(opts imagetypes.ProcessImageOptions, log *logger.CLILogger) error {
112113
if opts.AirgapBundle == "" {
113-
return &imagetypes.CopyAirgapImagesResult{}, nil
114+
return nil
114115
}
115116

116117
pushOpts := imagetypes.PushImagesOptions{
@@ -125,27 +126,25 @@ func CopyAirgapImages(opts imagetypes.ProcessImageOptions, log *logger.CLILogger
125126
LogForUI: true,
126127
}
127128

128-
copyResult, err := TagAndPushImagesFromBundle(opts.AirgapBundle, pushOpts)
129+
err := TagAndPushImagesFromBundle(opts.AirgapBundle, pushOpts)
129130
if err != nil {
130-
return nil, errors.Wrap(err, "failed to push images from bundle")
131+
return errors.Wrap(err, "failed to push images from bundle")
131132
}
132133

133-
return &imagetypes.CopyAirgapImagesResult{
134-
EmbeddedClusterArtifacts: copyResult.EmbeddedClusterArtifacts,
135-
}, nil
134+
return nil
136135
}
137136

138-
func TagAndPushImagesFromBundle(airgapBundle string, options imagetypes.PushImagesOptions) (*imagetypes.CopyAirgapImagesResult, error) {
137+
func TagAndPushImagesFromBundle(airgapBundle string, options imagetypes.PushImagesOptions) error {
139138
airgap, err := kotsutil.FindAirgapMetaInBundle(airgapBundle)
140139
if err != nil {
141-
return nil, errors.Wrap(err, "failed to find airgap meta")
140+
return errors.Wrap(err, "failed to find airgap meta")
142141
}
143142

144143
switch airgap.Spec.Format {
145144
case dockertypes.FormatDockerRegistry:
146145
extractedBundle, err := os.MkdirTemp("", "extracted-airgap-kots")
147146
if err != nil {
148-
return nil, errors.Wrap(err, "failed to create temp dir for unarchived airgap bundle")
147+
return errors.Wrap(err, "failed to create temp dir for unarchived airgap bundle")
149148
}
150149
defer os.RemoveAll(extractedBundle)
151150

@@ -155,34 +154,32 @@ func TagAndPushImagesFromBundle(airgapBundle string, options imagetypes.PushImag
155154
},
156155
}
157156
if err := tarGz.Unarchive(airgapBundle, extractedBundle); err != nil {
158-
return nil, errors.Wrap(err, "falied to unarchive airgap bundle")
157+
return errors.Wrap(err, "falied to unarchive airgap bundle")
159158
}
160159
if err := PushImagesFromTempRegistry(extractedBundle, airgap.Spec.SavedImages, options); err != nil {
161-
return nil, errors.Wrap(err, "failed to push images from docker registry bundle")
160+
return errors.Wrap(err, "failed to push images from docker registry bundle")
162161
}
163162
case dockertypes.FormatDockerArchive, "":
164163
if err := PushImagesFromDockerArchiveBundle(airgapBundle, options); err != nil {
165-
return nil, errors.Wrap(err, "failed to push images from docker archive bundle")
164+
return errors.Wrap(err, "failed to push images from docker archive bundle")
166165
}
167166
default:
168-
return nil, errors.Errorf("Airgap bundle format '%s' is not supported", airgap.Spec.Format)
167+
return errors.Errorf("Airgap bundle format '%s' is not supported", airgap.Spec.Format)
169168
}
170169

171170
pushEmbeddedArtifactsOpts := imagetypes.PushEmbeddedClusterArtifactsOptions{
172-
Registry: options.Registry,
173-
Tag: imageutil.SanitizeTag(fmt.Sprintf("%s-%s-%s", airgap.Spec.ChannelID, airgap.Spec.UpdateCursor, airgap.Spec.VersionLabel)),
174-
HTTPClient: orasretry.DefaultClient,
171+
Registry: options.Registry,
172+
ChannelID: airgap.Spec.ChannelID,
173+
UpdateCursor: airgap.Spec.UpdateCursor,
174+
VersionLabel: airgap.Spec.VersionLabel,
175+
HTTPClient: orasretry.DefaultClient,
175176
}
176-
pushedArtifacts, err := PushEmbeddedClusterArtifacts(airgapBundle, pushEmbeddedArtifactsOpts)
177+
err = PushEmbeddedClusterArtifacts(airgapBundle, airgap.Spec.EmbeddedClusterArtifacts, pushEmbeddedArtifactsOpts)
177178
if err != nil {
178-
return nil, errors.Wrap(err, "failed to push embedded cluster artifacts")
179-
}
180-
181-
result := &imagetypes.CopyAirgapImagesResult{
182-
EmbeddedClusterArtifacts: pushedArtifacts,
179+
return errors.Wrap(err, "failed to push embedded cluster artifacts")
183180
}
184181

185-
return result, nil
182+
return nil
186183
}
187184

188185
func PushImagesFromTempRegistry(airgapRootDir string, imageList []string, options imagetypes.PushImagesOptions) error {
@@ -684,70 +681,75 @@ func reportWriterWithProgress(imageInfos map[string]*imagetypes.ImageInfo, repor
684681
return pipeWriter
685682
}
686683

687-
func PushEmbeddedClusterArtifacts(airgapBundle string, opts imagetypes.PushEmbeddedClusterArtifactsOptions) ([]string, error) {
684+
func PushEmbeddedClusterArtifacts(airgapBundle string, artifactsToPush *kotsv1beta1.EmbeddedClusterArtifacts, opts imagetypes.PushEmbeddedClusterArtifactsOptions) error {
688685
tmpDir, err := os.MkdirTemp("", "embedded-cluster-artifacts")
689686
if err != nil {
690-
return nil, errors.Wrap(err, "failed to create temp directory")
687+
return errors.Wrap(err, "failed to create temp directory")
691688
}
692689
defer os.RemoveAll(tmpDir)
693690

694691
fileReader, err := os.Open(airgapBundle)
695692
if err != nil {
696-
return nil, errors.Wrap(err, "failed to open file")
693+
return errors.Wrap(err, "failed to open file")
697694
}
698695
defer fileReader.Close()
699696

700697
gzipReader, err := gzip.NewReader(fileReader)
701698
if err != nil {
702-
return nil, errors.Wrap(err, "failed to get new gzip reader")
699+
return errors.Wrap(err, "failed to get new gzip reader")
703700
}
704701
defer gzipReader.Close()
705702

706703
var artifacts []string
707704

708705
tarReader := tar.NewReader(gzipReader)
709-
pushedArtifacts := make([]string, 0)
710706
for {
711707
header, err := tarReader.Next()
712708
if err == io.EOF {
713709
break
714710
}
715711
if err != nil {
716-
return nil, errors.Wrap(err, "failed to get read archive")
712+
return errors.Wrap(err, "failed to get read archive")
717713
}
718714

719715
if header.Typeflag != tar.TypeReg {
720716
continue
721717
}
722718

723-
if filepath.Dir(header.Name) != "embedded-cluster" {
719+
if !shouldPushArtifact(header.Name, artifactsToPush) {
724720
continue
725721
}
726722

727723
dstFilePath := filepath.Join(tmpDir, header.Name)
728724
if err := os.MkdirAll(filepath.Dir(dstFilePath), 0755); err != nil {
729-
return nil, errors.Wrap(err, "failed to create path")
725+
return errors.Wrap(err, "failed to create path")
730726
}
731727

732728
dstFile, err := os.Create(dstFilePath)
733729
if err != nil {
734-
return nil, errors.Wrap(err, "failed to create file")
730+
return errors.Wrap(err, "failed to create file")
735731
}
736732

737733
if _, err := io.Copy(dstFile, tarReader); err != nil {
738734
dstFile.Close()
739-
return nil, errors.Wrap(err, "failed to copy file data")
735+
return errors.Wrap(err, "failed to copy file data")
740736
}
741737

742738
dstFile.Close()
743739
artifacts = append(artifacts, dstFilePath)
744740
}
745741

746742
for i, dstFilePath := range artifacts {
747-
name := filepath.Base(dstFilePath)
748-
repository := filepath.Join("embedded-cluster", imageutil.SanitizeRepo(name))
743+
ociArtifactPath := imageutil.NewEmbeddedClusterOCIArtifactPath(dstFilePath, imageutil.EmbeddedClusterArtifactOCIPathOptions{
744+
RegistryHost: opts.Registry.Endpoint,
745+
RegistryNamespace: opts.Registry.Namespace,
746+
ChannelID: opts.ChannelID,
747+
UpdateCursor: opts.UpdateCursor,
748+
VersionLabel: opts.VersionLabel,
749+
})
750+
749751
artifactFile := imagetypes.OCIArtifactFile{
750-
Name: name,
752+
Name: ociArtifactPath.Name,
751753
Path: dstFilePath,
752754
MediaType: EmbeddedClusterMediaType,
753755
}
@@ -756,20 +758,31 @@ func PushEmbeddedClusterArtifacts(airgapBundle string, opts imagetypes.PushEmbed
756758
Files: []imagetypes.OCIArtifactFile{artifactFile},
757759
ArtifactType: EmbeddedClusterArtifactType,
758760
Registry: opts.Registry,
759-
Repository: repository,
760-
Tag: opts.Tag,
761+
Repository: ociArtifactPath.Repository,
762+
Tag: ociArtifactPath.Tag,
761763
HTTPClient: opts.HTTPClient,
762764
}
763765

764766
fmt.Printf("Pushing embedded cluster artifacts (%d/%d)\n", i+1, len(artifacts))
765-
artifact := fmt.Sprintf("%s:%s", filepath.Join(opts.Registry.Endpoint, opts.Registry.Namespace, repository), opts.Tag)
766767
if err := pushOCIArtifact(pushOCIArtifactOpts); err != nil {
767-
return nil, errors.Wrapf(err, "failed to push oci artifact %s", name)
768+
return errors.Wrapf(err, "failed to push oci artifact %s", ociArtifactPath.Name)
768769
}
769-
pushedArtifacts = append(pushedArtifacts, artifact)
770770
}
771771

772-
return pushedArtifacts, nil
772+
return nil
773+
}
774+
775+
func shouldPushArtifact(artifactPath string, artifactsToPush *kotsv1beta1.EmbeddedClusterArtifacts) bool {
776+
if artifactsToPush == nil {
777+
return false
778+
}
779+
780+
switch artifactPath {
781+
case artifactsToPush.BinaryAmd64, artifactsToPush.Charts, artifactsToPush.ImagesAmd64, artifactsToPush.Metadata:
782+
return true
783+
default:
784+
return false
785+
}
773786
}
774787

775788
func pushOCIArtifact(opts imagetypes.PushOCIArtifactOptions) error {

0 commit comments

Comments
 (0)