diff --git a/pkg/base/write_images.go b/pkg/base/write_images.go index 3d3084a7ee..fec932f718 100644 --- a/pkg/base/write_images.go +++ b/pkg/base/write_images.go @@ -7,7 +7,9 @@ import ( kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1" "github.com/replicatedhq/kots/pkg/docker/registry" "github.com/replicatedhq/kots/pkg/image" + "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" kustomizeimage "sigs.k8s.io/kustomize/api/types" ) @@ -21,8 +23,7 @@ type WriteUpstreamImageOptions struct { IsAirgap bool Log *logger.CLILogger ReportWriter io.Writer - Installation *kotsv1beta1.Installation - Application *kotsv1beta1.Application + KotsKinds *kotsutil.KotsKinds } type WriteUpstreamImageResult struct { @@ -31,15 +32,34 @@ type WriteUpstreamImageResult struct { } func ProcessUpstreamImages(options WriteUpstreamImageOptions) (*WriteUpstreamImageResult, error) { + rewriteAll := options.IsAirgap additionalImages := make([]string, 0) - if options.Application != nil { - additionalImages = options.Application.Spec.AdditionalImages - } - checkedImages := makeImageInfoMap(options.Installation.Spec.KnownImages) + checkedImages := make(map[string]image.ImageInfo) - rewriteAll := options.IsAirgap - if options.Application != nil && options.Application.Spec.ProxyPublicImages { - rewriteAll = true + if options.KotsKinds != nil { + additionalImages = options.KotsKinds.KotsApplication.Spec.AdditionalImages + + collectors := make([]*troubleshootv1beta2.Collect, 0) + if options.KotsKinds.SupportBundle != nil { + collectors = append(collectors, options.KotsKinds.SupportBundle.Spec.Collectors...) + } + if options.KotsKinds.Collector != nil { + collectors = append(collectors, options.KotsKinds.Collector.Spec.Collectors...) + } + if options.KotsKinds.Preflight != nil { + collectors = append(collectors, options.KotsKinds.Preflight.Spec.Collectors...) + } + for _, c := range collectors { + if c.Run != nil && c.Run.Image != "" { + additionalImages = append(additionalImages, c.Run.Image) + } + } + + checkedImages = makeImageInfoMap(options.KotsKinds.Installation.Spec.KnownImages) + + if options.KotsKinds.KotsApplication.Spec.ProxyPublicImages { + rewriteAll = true + } } newImages, err := image.ProcessImages(options.SourceRegistry, options.DestRegistry, options.AppSlug, options.Log, options.ReportWriter, options.BaseDir, additionalImages, options.CopyImages, rewriteAll, checkedImages, options.DockerHubRegistry) diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go index cc1e2ffc02..ffaa624203 100644 --- a/pkg/pull/pull.go +++ b/pkg/pull/pull.go @@ -345,13 +345,13 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { files = append(files, file) } - newInstallation, err := upstream.LoadInstallation(u.GetUpstreamDir(writeUpstreamOptions)) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(u.GetUpstreamDir(writeUpstreamOptions)) if err != nil { - return "", errors.Wrap(err, "failed to load installation") + return "", errors.Wrap(err, "failed to load kotskinds") } - newInstallation.Spec.YAMLErrors = files + newKotsKinds.Installation.Spec.YAMLErrors = files - err = upstream.SaveInstallation(newInstallation, u.GetUpstreamDir(writeUpstreamOptions)) + err = upstream.SaveInstallation(&newKotsKinds.Installation, u.GetUpstreamDir(writeUpstreamOptions)) if err != nil { return "", errors.Wrap(err, "failed to save installation") } @@ -394,12 +394,12 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { return "", errors.Wrap(err, "failed to create new config context template builder") } - newInstallation, err := upstream.LoadInstallation(u.GetUpstreamDir(writeUpstreamOptions)) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(u.GetUpstreamDir(writeUpstreamOptions)) if err != nil { - return "", errors.Wrap(err, "failed to load installation") + return "", errors.Wrap(err, "failed to load kotskinds") } - err = crypto.InitFromString(newInstallation.Spec.EncryptionKey) + err = crypto.InitFromString(newKotsKinds.Installation.Spec.EncryptionKey) if err != nil { return "", errors.Wrap(err, "failed to load encryption cipher") } @@ -533,13 +533,9 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp // Rewrite all images if options.RewriteImageOptions.ImageFiles == "" { - newInstallation, err := upstream.LoadInstallation(upstreamDir) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(upstreamDir) if err != nil { - return nil, errors.Wrap(err, "failed to load installation") - } - newApplication, err := upstream.LoadApplication(upstreamDir) - if err != nil { - return nil, errors.Wrap(err, "failed to load application") + return nil, errors.Wrap(err, "failed to load kotskinds from new upstream") } writeUpstreamImageOptions := base.WriteUpstreamImageOptions{ @@ -554,8 +550,7 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp Password: dockerHubRegistryCreds.Password, }, ReportWriter: options.ReportWriter, - Installation: newInstallation, - Application: newApplication, + KotsKinds: newKotsKinds, CopyImages: !options.RewriteImageOptions.IsReadOnly, } if license != nil { @@ -579,9 +574,9 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp } images = copyResult.Images - newInstallation.Spec.KnownImages = copyResult.CheckedImages + newKotsKinds.Installation.Spec.KnownImages = copyResult.CheckedImages - err = upstream.SaveInstallation(newInstallation, upstreamDir) + err = upstream.SaveInstallation(&newKotsKinds.Installation, upstreamDir) if err != nil { return nil, errors.Wrap(err, "failed to save installation") } @@ -614,7 +609,7 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp processUpstreamImageOptions.ReplicatedRegistry.Password = license.Spec.LicenseID } - var rewrittenImages []kustomizetypes.Image + var upstreamImages *upstream.ProcessUpstreamImageResult if images == nil { // don't do ProcessUpstreamImages if we already copied them imagesData, err := ioutil.ReadFile(filepath.Join(options.AirgapRoot, "images.json")) if err != nil && !os.IsNotExist(err) { @@ -630,7 +625,7 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp processUpstreamImageOptions.KnownImages = images } - rewrittenImages, err = upstream.ProcessUpstreamImages(u, processUpstreamImageOptions) + upstreamImages, err = upstream.ProcessUpstreamImages(u, processUpstreamImageOptions) if err != nil { return nil, errors.Wrap(err, "failed to push upstream images") } @@ -651,7 +646,7 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp // For the newer style charts, create a new secret per chart as helm adds chart specific // details to annotations and labels to it. for _, v := range writeMidstreamOptions.NewHelmCharts { - if filepath.Base(b.Path) == v.Spec.Chart.Name && v.Spec.UseHelmInstall == true { + if filepath.Base(b.Path) == v.Spec.Chart.Name && v.Spec.UseHelmInstall { namePrefix = fmt.Sprintf("%s-%s", options.AppSlug, filepath.Base(b.Path)) break } @@ -667,26 +662,28 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp return nil, errors.Wrap(err, "create pull secret") } - if rewrittenImages != nil { - images = rewrittenImages + if upstreamImages != nil { + images = upstreamImages.KustomizeImages + + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(upstreamDir) + if err != nil { + return nil, errors.Wrap(err, "failed to load kotskinds from new upstream") + } + newKotsKinds.Installation.Spec.KnownImages = upstreamImages.KnownImages + err = upstream.SaveInstallation(&newKotsKinds.Installation, upstreamDir) + if err != nil { + return nil, errors.Wrap(err, "failed to save installation for private registry") + } } objects = affectedObjects } } else if license != nil { - newInstallation, err := upstream.LoadInstallation(upstreamDir) - if err != nil { - return nil, errors.Wrap(err, "failed to load installation") - } - - application, err := upstream.LoadApplication(upstreamDir) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(upstreamDir) if err != nil { - return nil, errors.Wrap(err, "failed to load application") + return nil, errors.Wrap(err, "failed to load kotskinds") } - allPrivate := false - if application != nil { - allPrivate = application.Spec.ProxyPublicImages - } + allPrivate := newKotsKinds.KotsApplication.Spec.ProxyPublicImages // Rewrite private images findPrivateImagesOptions := base.FindPrivateImagesOptions{ @@ -700,7 +697,7 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp Username: dockerHubRegistryCreds.Username, Password: dockerHubRegistryCreds.Password, }, - Installation: newInstallation, + Installation: &newKotsKinds.Installation, AllImagesPrivate: allPrivate, UseHelmInstall: writeMidstreamOptions.UseHelmInstall, } @@ -709,8 +706,8 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options PullOp return nil, errors.Wrap(err, "failed to find private images") } - newInstallation.Spec.KnownImages = findResult.CheckedImages - err = upstream.SaveInstallation(newInstallation, upstreamDir) + newKotsKinds.Installation.Spec.KnownImages = findResult.CheckedImages + err = upstream.SaveInstallation(&newKotsKinds.Installation, upstreamDir) if err != nil { return nil, errors.Wrap(err, "failed to save installation") } diff --git a/pkg/registry/images.go b/pkg/registry/images.go index 11b6ecc814..6c6e591200 100644 --- a/pkg/registry/images.go +++ b/pkg/registry/images.go @@ -253,7 +253,7 @@ func deleteUnusedImages(ctx context.Context, registry types.RegistrySettings, us } } - for _, i := range usedImages { + for _, usedImage := range usedImages { registryOptions := dockerregistry.RegistryOptions{ Endpoint: registry.Hostname, Namespace: registry.Namespace, @@ -261,7 +261,7 @@ func deleteUnusedImages(ctx context.Context, registry types.RegistrySettings, us Password: registry.Password, } - appImage := image.DestRef(registryOptions, i) + appImage := image.DestRef(registryOptions, usedImage) appImageRef, err := docker.ParseReference(fmt.Sprintf("//%s", appImage)) if err != nil { return errors.Wrapf(err, "failed to parse %s", appImage) @@ -356,6 +356,7 @@ func runGCCommand(ctx context.Context) error { // we don't care if this file exists, so just ignore errors for now _ = uploadEmptyFileToRegistry(ctx) + errs := make([]error, 0) for _, pod := range registryPods.Items { req := clientset.CoreV1().RESTClient().Post().Resource("pods").Name(pod.Name).Namespace(pod.Namespace).SubResource("exec") parameterCodec := runtime.NewParameterCodec(scheme) @@ -370,7 +371,8 @@ func runGCCommand(ctx context.Context) error { exec, err := remotecommand.NewSPDYExecutor(clusterConfig, "POST", req.URL()) if err != nil { - return errors.Wrap(err, "failed to create remote executor") + errs = append(errs, errors.Wrap(err, "failed to create remote executor")) + continue } stdout := new(bytes.Buffer) @@ -387,12 +389,15 @@ func runGCCommand(ctx context.Context) error { logger.Infof("garbage collect command stderr: %s", stderr.Bytes()) if err != nil { - return errors.Wrap(err, "failed to stream command output") + errs = append(errs, errors.Wrap(err, "failed to stream command output")) + continue } + // terminate after the first successful loop iteration return nil } + logger.Errorf("errors while running garbage collect command: %v", errs) return errors.New("no pods found to run garbage collect command") } diff --git a/pkg/rewrite/rewrite.go b/pkg/rewrite/rewrite.go index 8cac3628c6..9ed8d2d3af 100644 --- a/pkg/rewrite/rewrite.go +++ b/pkg/rewrite/rewrite.go @@ -154,13 +154,13 @@ func Rewrite(rewriteOptions RewriteOptions) error { files = append(files, file) } - newInstallation, err := upstream.LoadInstallation(u.GetUpstreamDir(writeUpstreamOptions)) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(u.GetUpstreamDir(writeUpstreamOptions)) if err != nil { return errors.Wrap(err, "failed to load installation") } - newInstallation.Spec.YAMLErrors = files + newKotsKinds.Installation.Spec.YAMLErrors = files - err = upstream.SaveInstallation(newInstallation, u.GetUpstreamDir(writeUpstreamOptions)) + err = upstream.SaveInstallation(&newKotsKinds.Installation, u.GetUpstreamDir(writeUpstreamOptions)) if err != nil { return errors.Wrap(err, "failed to save installation") } @@ -341,13 +341,9 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options Rewrit // When only registry endpoint is set, we don't need to copy images, but still // need to rewrite them and create secrets. - newInstallation, err := upstream.LoadInstallation(upstreamDir) + newKotsKinds, err := kotsutil.LoadKotsKindsFromPath(upstreamDir) if err != nil { - return nil, errors.Wrap(err, "failed to load installation") - } - application, err := upstream.LoadApplication(upstreamDir) - if err != nil { - return nil, errors.Wrap(err, "failed to load application") + return nil, errors.Wrap(err, "failed to load kotskinds from new upstream") } writeUpstreamImageOptions := base.WriteUpstreamImageOptions{ @@ -368,10 +364,9 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options Rewrit Username: dockerHubRegistryCreds.Username, Password: dockerHubRegistryCreds.Password, }, - Installation: newInstallation, - Application: application, - IsAirgap: options.IsAirgap, - CopyImages: options.CopyImages, + KotsKinds: newKotsKinds, + IsAirgap: options.IsAirgap, + CopyImages: options.CopyImages, } if license != nil { @@ -385,8 +380,8 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options Rewrit return nil, errors.Wrap(err, "failed to write upstream images") } - newInstallation.Spec.KnownImages = copyResult.CheckedImages - err = upstream.SaveInstallation(newInstallation, upstreamDir) + newKotsKinds.Installation.Spec.KnownImages = copyResult.CheckedImages + err = upstream.SaveInstallation(&newKotsKinds.Installation, upstreamDir) if err != nil { return nil, errors.Wrap(err, "failed to save installation") } @@ -424,15 +419,12 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options Rewrit images = copyResult.Images objects = affectedObjects } else { - application, err := upstream.LoadApplication(upstreamDir) + kotsKinds, err := kotsutil.LoadKotsKindsFromPath(upstreamDir) if err != nil { - return nil, errors.Wrap(err, "failed to load application") + return nil, errors.Wrap(err, "failed to load kotskonds") } - allPrivate := false - if application != nil { - allPrivate = application.Spec.ProxyPublicImages - } + allPrivate := kotsKinds.KotsApplication.Spec.ProxyPublicImages // When CopyImages is not set, we only rewrite private images and use license to create secrets // for all objects that have private images @@ -456,12 +448,8 @@ func writeMidstream(writeMidstreamOptions midstream.WriteOptions, options Rewrit return nil, errors.Wrap(err, "failed to find private images") } - newInstallation, err := upstream.LoadInstallation(upstreamDir) - if err != nil { - return nil, errors.Wrap(err, "failed to load installation") - } - newInstallation.Spec.KnownImages = findResult.CheckedImages - err = upstream.SaveInstallation(newInstallation, upstreamDir) + kotsKinds.Installation.Spec.KnownImages = findResult.CheckedImages + err = upstream.SaveInstallation(&kotsKinds.Installation, upstreamDir) if err != nil { return nil, errors.Wrap(err, "failed to save installation") } diff --git a/pkg/upstream/installation.go b/pkg/upstream/installation.go index 85c5553383..5bab1a6b1c 100644 --- a/pkg/upstream/installation.go +++ b/pkg/upstream/installation.go @@ -2,72 +2,12 @@ package upstream import ( "io/ioutil" - "os" "path" - "path/filepath" "github.com/pkg/errors" kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1" - "k8s.io/client-go/kubernetes/scheme" ) -func LoadApplication(upstreamDir string) (*kotsv1beta1.Application, error) { - var application *kotsv1beta1.Application - - err := filepath.Walk(upstreamDir, - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() { - return nil - } - - content, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(content, nil, nil) - if err != nil { - return nil - } - - if gvk.Group == "kots.io" && gvk.Version == "v1beta1" && gvk.Kind == "Application" { - application = obj.(*kotsv1beta1.Application) - } - - return nil - }) - - if err != nil { - return nil, errors.Wrap(err, "failed to walk archive dir") - } - - return application, nil -} - -func LoadInstallation(upstreamDir string) (*kotsv1beta1.Installation, error) { - content, err := ioutil.ReadFile(path.Join(upstreamDir, "userdata", "installation.yaml")) - if err != nil { - return nil, errors.Wrap(err, "failed to read existing installation") - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(content, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to decode installation") - } - - if gvk.Group == "kots.io" && gvk.Version == "v1beta1" && gvk.Kind == "Installation" { - return obj.(*kotsv1beta1.Installation), nil - } - - return nil, errors.Errorf("unexpected gvk in installation file: %s/%s/%s", gvk.Group, gvk.Version, gvk.Kind) -} - func SaveInstallation(installation *kotsv1beta1.Installation, upstreamDir string) error { filename := path.Join(upstreamDir, "userdata", "installation.yaml") err := ioutil.WriteFile(filename, mustMarshalInstallation(installation), 0644) diff --git a/pkg/upstream/push_images.go b/pkg/upstream/push_images.go index e5ef61abb5..a954aeda3e 100644 --- a/pkg/upstream/push_images.go +++ b/pkg/upstream/push_images.go @@ -5,6 +5,7 @@ import ( "time" "github.com/pkg/errors" + kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1" "github.com/replicatedhq/kots/pkg/docker/registry" "github.com/replicatedhq/kots/pkg/image" "github.com/replicatedhq/kots/pkg/kotsadm" @@ -28,7 +29,12 @@ type ProcessUpstreamImagesOptions struct { DestinationRegistry registry.RegistryOptions } -func ProcessUpstreamImages(u *types.Upstream, options ProcessUpstreamImagesOptions) ([]kustomizetypes.Image, error) { +type ProcessUpstreamImageResult struct { + KustomizeImages []kustomizetypes.Image + KnownImages []kotsv1beta1.InstallationImage +} + +func ProcessUpstreamImages(u *types.Upstream, options ProcessUpstreamImagesOptions) (*ProcessUpstreamImageResult, error) { pushOpts := kotsadmtypes.PushImagesOptions{ Registry: options.DestinationRegistry, Log: options.Log, @@ -73,7 +79,13 @@ func ProcessUpstreamImages(u *types.Upstream, options ProcessUpstreamImagesOptio withAltNames = append(withAltNames, image.BuildImageAltNames(i)...) } - return withAltNames, nil + result := &ProcessUpstreamImageResult{ + KustomizeImages: withAltNames, + // This list is slightly different from the list we get from app specs because of alternative names, + // but it still works because after rewriting image names with private registry, the lists become the same. + KnownImages: makeInstallationImages(withAltNames), + } + return result, nil } type ProgressReport struct { @@ -101,3 +113,14 @@ type ProgressImage struct { // time when image finished uploading EndTime time.Time `json:"endTime"` } + +func makeInstallationImages(images []kustomizetypes.Image) []kotsv1beta1.InstallationImage { + result := make([]kotsv1beta1.InstallationImage, 0) + for _, image := range images { + result = append(result, kotsv1beta1.InstallationImage{ + Image: image.Name, + IsPrivate: true, + }) + } + return result +}