Skip to content

Commit cf297d4

Browse files
Simon Emmsroboquat
Simon Emms
authored andcommitted
[installer]: add a mirror kots command to generate the additional images
The additional images are a way of telling Replicated which images are required to make an air-gapped installation
1 parent 9cd8868 commit cf297d4

File tree

4 files changed

+1216
-136
lines changed

4 files changed

+1216
-136
lines changed

install/installer/cmd/mirror_kots.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the MIT License. See License-MIT.txt in the project root for license information.
3+
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
"io/ioutil"
9+
10+
"github.com/gitpod-io/gitpod/installer/pkg/config"
11+
configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
12+
kots "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
13+
"github.com/spf13/cobra"
14+
"sigs.k8s.io/yaml"
15+
)
16+
17+
var mirrorKotsOpts struct {
18+
File string
19+
}
20+
21+
// mirrorKotsCmd represents the mirror kots command
22+
var mirrorKotsCmd = &cobra.Command{
23+
Use: "kots",
24+
Short: "Renders a list of images used to the KOTS app file",
25+
Long: `Renders a list of images used to the KOTS app file
26+
27+
The KOTS application file allows an optional array of strings that
28+
reference images. These are used to build the air gap bundle and are
29+
pushed to the local registry during installation.
30+
31+
KOTS documentation:
32+
https://docs.replicated.com/reference/custom-resource-application#additionalimages`,
33+
Example: "gitpod-installer mirror kots --file ../kots/manifests/kots-app.yaml",
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
// Build a virtual config file
36+
rawCfg, cfgVersion, err := config.Load("")
37+
if err != nil {
38+
return err
39+
}
40+
cfg := rawCfg.(*configv1.Config)
41+
42+
if mirrorKotsOpts.File == "" {
43+
return fmt.Errorf("kots file must be defined")
44+
}
45+
46+
kotsBytes, err := ioutil.ReadFile(mirrorKotsOpts.File)
47+
if err != nil {
48+
panic(fmt.Sprintf("couldn't read file %s, %s", mirrorKotsOpts.File, err))
49+
}
50+
51+
var kotsApp kots.Application
52+
err = yaml.Unmarshal(kotsBytes, &kotsApp)
53+
if err != nil {
54+
return err
55+
}
56+
57+
// Fake the required config data
58+
cfg.Domain = "gitpod.io"
59+
cfg.Repository = "custom-repo-name"
60+
61+
images, err := generateMirrorList(cfgVersion, cfg)
62+
if err != nil {
63+
return err
64+
}
65+
66+
// Only append images - this will keep any existing images in the spec
67+
for _, img := range images {
68+
kotsApp.Spec.AdditionalImages = append(kotsApp.Spec.AdditionalImages, img.Original)
69+
}
70+
71+
fc, err := yaml.Marshal(kotsApp)
72+
if err != nil {
73+
return err
74+
}
75+
76+
err = ioutil.WriteFile(mirrorKotsOpts.File, fc, 0644)
77+
if err != nil {
78+
return err
79+
}
80+
81+
fmt.Println("Gitpod images written to " + mirrorKotsOpts.File)
82+
83+
return nil
84+
},
85+
}
86+
87+
func init() {
88+
mirrorCmd.AddCommand(mirrorKotsCmd)
89+
90+
mirrorKotsCmd.Flags().StringVarP(&mirrorKotsOpts.File, "file", "f", "", "path to the kots app file")
91+
}

install/installer/cmd/mirror_list.go

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/docker/distribution/reference"
1515
"github.com/gitpod-io/gitpod/installer/pkg/common"
16+
configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
1617
"github.com/spf13/cobra"
1718
)
1819

@@ -61,67 +62,11 @@ image to the "target" repo`,
6162
return err
6263
}
6364

64-
// Throw error if set to the default Gitpod repository
65-
if cfg.Repository == common.GitpodContainerRegistry {
66-
return fmt.Errorf("cannot mirror images to repository %s", common.GitpodContainerRegistry)
67-
}
68-
69-
// Get the target repository from the config
70-
targetRepo := strings.TrimRight(cfg.Repository, "/")
71-
72-
// Use the default Gitpod registry to pull from
73-
cfg.Repository = common.GitpodContainerRegistry
74-
75-
k8s, err := renderKubernetesObjects(cfgVersion, cfg)
65+
images, err := generateMirrorList(cfgVersion, cfg)
7666
if err != nil {
7767
return err
7868
}
7969

80-
// Map of images used for deduping
81-
allImages := make(map[string]bool)
82-
83-
rawImages := make([]string, 0)
84-
for _, item := range k8s {
85-
rawImages = append(rawImages, getPodImages(item)...)
86-
rawImages = append(rawImages, getGenericImages(item)...)
87-
}
88-
89-
images := make([]mirrorListRepo, 0)
90-
for _, img := range rawImages {
91-
// Dedupe
92-
if _, ok := allImages[img]; ok {
93-
continue
94-
}
95-
allImages[img] = true
96-
97-
// Convert target
98-
target := img
99-
if strings.Contains(img, cfg.Repository) {
100-
// This is the Gitpod registry
101-
target = strings.Replace(target, cfg.Repository, targetRepo, 1)
102-
} else if !mirrorListOpts.ExcludeThirdParty {
103-
// Amend third-party images - remove the first part
104-
thirdPartyImg := strings.Join(strings.Split(img, "/")[1:], "/")
105-
target = fmt.Sprintf("%s/%s", targetRepo, thirdPartyImg)
106-
} else {
107-
// Excluding third-party images - just skip this one
108-
continue
109-
}
110-
111-
images = append(images, mirrorListRepo{
112-
Original: img,
113-
Target: target,
114-
})
115-
}
116-
117-
// Sort it by the Original
118-
sort.Slice(images, func(i, j int) bool {
119-
scoreI := images[i].Original
120-
scoreJ := images[j].Original
121-
122-
return scoreI < scoreJ
123-
})
124-
12570
fc, err := common.ToJSONString(images)
12671
if err != nil {
12772
return err
@@ -140,6 +85,71 @@ func init() {
14085
mirrorListCmd.Flags().StringVarP(&mirrorListOpts.ConfigFN, "config", "c", os.Getenv("GITPOD_INSTALLER_CONFIG"), "path to the config file")
14186
}
14287

88+
func generateMirrorList(cfgVersion string, cfg *configv1.Config) ([]mirrorListRepo, error) {
89+
// Throw error if set to the default Gitpod repository
90+
if cfg.Repository == common.GitpodContainerRegistry {
91+
return nil, fmt.Errorf("cannot mirror images to repository %s", common.GitpodContainerRegistry)
92+
}
93+
94+
// Get the target repository from the config
95+
targetRepo := strings.TrimRight(cfg.Repository, "/")
96+
97+
// Use the default Gitpod registry to pull from
98+
cfg.Repository = common.GitpodContainerRegistry
99+
100+
k8s, err := renderKubernetesObjects(cfgVersion, cfg)
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
// Map of images used for deduping
106+
allImages := make(map[string]bool)
107+
108+
rawImages := make([]string, 0)
109+
for _, item := range k8s {
110+
rawImages = append(rawImages, getPodImages(item)...)
111+
rawImages = append(rawImages, getGenericImages(item)...)
112+
}
113+
114+
images := make([]mirrorListRepo, 0)
115+
for _, img := range rawImages {
116+
// Dedupe
117+
if _, ok := allImages[img]; ok {
118+
continue
119+
}
120+
allImages[img] = true
121+
122+
// Convert target
123+
target := img
124+
if strings.Contains(img, cfg.Repository) {
125+
// This is the Gitpod registry
126+
target = strings.Replace(target, cfg.Repository, targetRepo, 1)
127+
} else if !mirrorListOpts.ExcludeThirdParty {
128+
// Amend third-party images - remove the first part
129+
thirdPartyImg := strings.Join(strings.Split(img, "/")[1:], "/")
130+
target = fmt.Sprintf("%s/%s", targetRepo, thirdPartyImg)
131+
} else {
132+
// Excluding third-party images - just skip this one
133+
continue
134+
}
135+
136+
images = append(images, mirrorListRepo{
137+
Original: img,
138+
Target: target,
139+
})
140+
}
141+
142+
// Sort it by the Original
143+
sort.Slice(images, func(i, j int) bool {
144+
scoreI := images[i].Original
145+
scoreJ := images[j].Original
146+
147+
return scoreI < scoreJ
148+
})
149+
150+
return images, nil
151+
}
152+
143153
// getGenericImages this is a bit brute force - anything starting "docker.io" or with Gitpod repo is found
144154
// this will be in ConfigMaps and could be anything, so will need cleaning up
145155
func getGenericImages(k8sObj string) []string {

0 commit comments

Comments
 (0)