1 package main
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "net/http"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "text/template"
11 "time"
12
13 "github.com/pkg/errors"
14 "github.com/spf13/cobra"
15
16 "github.com/datawire/ambassador/v2/pkg/k8s"
17 "github.com/datawire/ambassador/v2/pkg/kubeapply"
18 )
19
20 var tmpl = template.Must(template.
21 New("docker-registry.yaml").
22 Parse(`---
23 apiVersion: v1
24 kind: Namespace
25 metadata:
26 name: docker-registry
27 ---
28 apiVersion: v1
29 kind: Service
30 metadata:
31 namespace: docker-registry
32 name: registry
33 spec:
34 type: NodePort
35 selector:
36 app: registry
37 ports:
38 - port: 5000
39 nodePort: 31000
40 ---
41 apiVersion: apps/v1
42 # XXX: Avoid using a StatefulSet if possible, because kubeapply
43 # doesn't know how to wait for them.
44 kind: {{ if eq .Storage "pvc" }}StatefulSet{{ else }}Deployment{{ end }}
45 metadata:
46 namespace: docker-registry
47 name: registry
48 spec:
49 replicas: 1
50 {{ if eq .Storage "pvc" }} # XXX: StatefulSet
51 serviceName: registry
52 {{ end }}
53 selector:
54 matchLabels:
55 app: registry
56 template:
57 metadata:
58 name: registry
59 labels:
60 app: registry
61 spec:
62 containers:
63 - name: registry
64 image: docker.io/library/registry:2
65 ports:
66 - containerPort: 5000
67 volumeMounts:
68 - mountPath: /var/lib/registry
69 name: registry-data
70 volumes:
71 - name: registry-data
72 {{ if eq .Storage "pvc" | not }}
73 # On Kubeception clusters, there is only 1 node, so a
74 # hostPath is fine.
75 hostPath:
76 path: /var/lib/registry
77 {{ else }}
78 persistentVolumeClaim:
79 claimName: registry-data
80 ---
81 apiVersion: v1
82 kind: PersistentVolumeClaim
83 metadata:
84 name: registry-data
85 namespace: docker-registry
86 spec:
87 accessModes:
88 - ReadWriteOnce
89 resources:
90 requests:
91 storage: 10Gi
92 {{ end }}
93 `))
94
95 func main() {
96 argparser := &cobra.Command{
97 Use: os.Args[0],
98 Short: "Manage a private in-cluster registry",
99 SilenceErrors: true,
100 SilenceUsage: true,
101 }
102 argparser.AddCommand(func() *cobra.Command {
103 var (
104 argStorage string
105 )
106 subparser := &cobra.Command{
107 Use: "up",
108 Short: "Initialize the registry, and create a port-forward to it",
109 Args: cobra.ExactArgs(0),
110 RunE: func(cobraCmd *cobra.Command, _ []string) error {
111 var kpfTarget string
112 switch argStorage {
113 case "pvc":
114 kpfTarget = "statefulset/registry"
115 case "hostPath":
116 kpfTarget = "deployment/registry"
117 default:
118 return errors.Errorf("invalid --storage=%q: must be one of 'pvc' or 'hostPath'", argStorage)
119 }
120
121 kubeinfo := k8s.NewKubeInfo("", "", "")
122
123
124
125
126 tmpdir, err := ioutil.TempDir("", filepath.Base(os.Args[0]))
127 if err != nil {
128 return err
129 }
130 defer os.RemoveAll(tmpdir)
131 yamlFile, err := os.OpenFile(filepath.Join(tmpdir, "docker-registry.yaml"), os.O_CREATE|os.O_WRONLY, 0600)
132 if err != nil {
133 return err
134 }
135 err = tmpl.Execute(yamlFile, map[string]interface{}{
136 "Storage": argStorage,
137 })
138 yamlFile.Close()
139 if err != nil {
140 return err
141 }
142 err = kubeapply.Kubeapply(
143 cobraCmd.Context(),
144 kubeinfo,
145 time.Minute,
146 false,
147 false,
148 yamlFile.Name(),
149 )
150 if err != nil {
151 return err
152 }
153
154
155 args, err := kubeinfo.GetKubectlArray("port-forward",
156 "--namespace=docker-registry",
157 kpfTarget,
158 "31000:5000")
159 if err != nil {
160 return err
161 }
162 cmd := exec.Command("kubectl", args...)
163 cmd.Stdout, err = os.OpenFile(filepath.Join(os.TempDir(), filepath.Base(os.Args[0])+".log"), os.O_CREATE|os.O_WRONLY, 0666)
164 if err != nil {
165 return err
166 }
167 cmd.Stderr = cmd.Stdout
168 if err := cmd.Start(); err != nil {
169 return err
170 }
171 for {
172 _, httpErr := http.Get("http://localhost:31000/")
173 if httpErr == nil {
174 fmt.Fprintln(os.Stderr, "port-forward ready")
175 break
176 } else {
177 fmt.Fprintln(os.Stderr, "waiting for port-forward to become ready...")
178 time.Sleep(time.Second)
179 }
180 }
181 return nil
182 },
183 }
184 subparser.Flags().StringVar(&argStorage, "storage", "", "Which type of storage to use ('pvc' or 'hostPath')")
185 return subparser
186 }())
187 argparser.AddCommand(&cobra.Command{
188 Use: "down",
189 Short: "Shut down the port-forward to the registry",
190 Args: cobra.ExactArgs(0),
191 RunE: func(_ *cobra.Command, _ []string) error {
192 cmd := exec.Command("killall", "kubectl")
193 cmd.Stdout = os.Stderr
194 cmd.Stderr = os.Stderr
195 return cmd.Run()
196 },
197 })
198 if err := argparser.Execute(); err != nil {
199 fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err)
200 os.Exit(1)
201 }
202 }
203
View as plain text