Skip to content

Commit 66035ee

Browse files
author
Simon Emms
committed
[installer]: output all images to allow easy mirroring of containers
The output gives the original image name and tag and generates the new image name to be used
1 parent 1808ad3 commit 66035ee

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

installer/cmd/render-mirror.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License-AGPL.txt in the project root for license information.
4+
5+
package cmd
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
"github.com/docker/distribution/reference"
11+
"github.com/gitpod-io/gitpod/installer/pkg/common"
12+
"github.com/spf13/cobra"
13+
"regexp"
14+
"sigs.k8s.io/yaml"
15+
"sort"
16+
"strings"
17+
)
18+
19+
type mirrorRepo struct {
20+
Original string `json:"original"`
21+
Target string `json:"target"`
22+
}
23+
24+
var mirrorOpts struct {
25+
ExcludeThirdParty bool
26+
Output string
27+
}
28+
29+
var outputTypes = []string{"yaml", "json"}
30+
31+
// mirrorCmd represents the mirror command
32+
var mirrorCmd = &cobra.Command{
33+
Use: "mirror",
34+
Short: "Renders a list of images used so they can be mirrored to a third-party registry",
35+
Long: `Renders a list of images used so they can be mirrored to a third-party registry
36+
37+
A config file is required which can be generated with the init command.
38+
39+
The output can then be used to iterate over each image. A script can
40+
be written to pull from the "original" path and then tag and push the
41+
image to the "target" repo`,
42+
Example: `
43+
# YAML
44+
gitpod-installer render mirror --config config.yaml > mirror.yaml
45+
46+
# JSON
47+
gitpod-installer render mirror --config config.yaml -o json > mirror.json
48+
49+
# Pull original and push to target
50+
for row in $(gitpod-installer render mirror --config ./config.yaml ./versions.yaml -o json | jq -c '.[]'); do
51+
original=$(echo $row | jq -r '.original')
52+
target=$(echo $row | jq -r '.target')
53+
docker pull $original
54+
docker tag $original $target
55+
docker push $target
56+
done`,
57+
RunE: func(cmd *cobra.Command, args []string) error {
58+
// Validate output type
59+
var output string
60+
for _, i := range outputTypes {
61+
if mirrorOpts.Output == i {
62+
output = i
63+
}
64+
}
65+
if output == "" {
66+
return fmt.Errorf("unknown output type: %s", mirrorOpts.Output)
67+
}
68+
69+
_, cfgVersion, cfg, err := loadConfig(renderOpts.ConfigFN)
70+
if err != nil {
71+
return err
72+
}
73+
74+
// Get the target repository from the config
75+
targetRepo := strings.TrimRight(cfg.Repository, "/")
76+
77+
// Use the default Gitpod registry to pull from
78+
cfg.Repository = common.GitpodContainerRegistry
79+
80+
k8s, err := renderKubernetesObjects(cfgVersion, cfg)
81+
if err != nil {
82+
return err
83+
}
84+
85+
// Map of images used for deduping
86+
allImages := make(map[string]bool)
87+
88+
rawImages := make([]string, 0)
89+
for _, item := range k8s {
90+
rawImages = append(rawImages, getPodImages(item)...)
91+
rawImages = append(rawImages, getGenericImages(item)...)
92+
}
93+
94+
images := make([]mirrorRepo, 0)
95+
for _, img := range rawImages {
96+
// Dedupe
97+
if _, ok := allImages[img]; ok {
98+
continue
99+
}
100+
allImages[img] = true
101+
102+
// Convert target
103+
target := img
104+
if strings.Contains(img, cfg.Repository) {
105+
// This is the Gitpod registry
106+
target = strings.Replace(target, cfg.Repository, targetRepo, 1)
107+
} else if !mirrorOpts.ExcludeThirdParty {
108+
// Wrap third-party images - remove the first part
109+
thirdPartyImg := strings.Join(strings.Split(img, "/")[1:], "/")
110+
target = fmt.Sprintf("%s/%s", targetRepo, thirdPartyImg)
111+
}
112+
113+
images = append(images, mirrorRepo{
114+
Original: img,
115+
Target: target,
116+
})
117+
}
118+
119+
// Sort it by the Original
120+
sort.Slice(images, func(i, j int) bool {
121+
scoreI := images[i].Original
122+
scoreJ := images[j].Original
123+
124+
return scoreI < scoreJ
125+
})
126+
127+
switch output {
128+
case "yaml":
129+
fc, err := yaml.Marshal(images)
130+
if err != nil {
131+
return err
132+
}
133+
134+
fmt.Printf("---\n# Gitpod\n%s", string(fc))
135+
case "json":
136+
fc, err := json.MarshalIndent(images, "", " ")
137+
if err != nil {
138+
return err
139+
}
140+
141+
fmt.Println(string(fc))
142+
}
143+
144+
return nil
145+
},
146+
}
147+
148+
func init() {
149+
renderCmd.AddCommand(mirrorCmd)
150+
151+
mirrorCmd.Flags().BoolVar(&mirrorOpts.ExcludeThirdParty, "exclude-third-party", false, "exclude non-Gitpod images")
152+
mirrorCmd.Flags().StringVarP(&mirrorOpts.Output, "output", "o", "yaml", fmt.Sprintf("output type - [%s]", strings.Join(outputTypes, ", ")))
153+
}
154+
155+
// getGenericImages this is a bit brute force - anything starting "docker.io" or with Gitpod repo is found
156+
// this will be in ConfigMaps and could be anything, so will need cleaning up
157+
func getGenericImages(k8sObj string) []string {
158+
var images []string
159+
160+
// Search for anything that matches docker.io or the Gitpod repo - docker.io needed for gitpod/workspace-full
161+
re := regexp.MustCompile(fmt.Sprintf("%s(.*)|%s(.*)", "docker.io", common.GitpodContainerRegistry))
162+
img := re.FindAllString(k8sObj, -1)
163+
164+
if len(img) > 0 {
165+
for _, i := range img {
166+
// Remove whitespace
167+
i = strings.TrimSpace(i)
168+
// Remove end commas
169+
i = strings.TrimRight(i, ",")
170+
// Remove wrapping quotes
171+
i = strings.Trim(i, "\"")
172+
// Validate the image - assumes images are already fully qualified names
173+
_, err := reference.ParseNamed(i)
174+
if err != nil {
175+
// Invalid - ignore
176+
continue
177+
}
178+
179+
images = append(images, i)
180+
}
181+
}
182+
183+
return images
184+
}
185+
186+
// getPodImages these are images that are found in the "image:" tag in a PodSpec
187+
// may be multiple tags in a file
188+
func getPodImages(k8sObj string) []string {
189+
var images []string
190+
191+
re := regexp.MustCompile("image:(.*)")
192+
img := re.FindAllString(k8sObj, -1)
193+
194+
if len(img) > 0 {
195+
for _, i := range img {
196+
// Remove "image":
197+
i = re.ReplaceAllString(i, "$1")
198+
// Remove whitespace
199+
i = strings.TrimSpace(i)
200+
// Remove wrapping quotes
201+
i = strings.Trim(i, "\"")
202+
203+
images = append(images, i)
204+
}
205+
}
206+
207+
return images
208+
}

0 commit comments

Comments
 (0)