-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
Copy pathconfig.go
243 lines (218 loc) · 7.89 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package containerd
import (
"fmt"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/containerd/containerd/remotes/docker"
"github.com/k3s-io/k3s/pkg/agent/templates"
util2 "github.com/k3s-io/k3s/pkg/agent/util"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/spegel"
"github.com/k3s-io/k3s/pkg/version"
"github.com/rancher/wharfie/pkg/registries"
"github.com/sirupsen/logrus"
)
type HostConfigs map[string]templates.HostConfig
// writeContainerdConfig renders and saves config.toml from the filled template
func writeContainerdConfig(cfg *config.Node, containerdConfig templates.ContainerdConfig) error {
var containerdTemplate string
containerdTemplateBytes, err := os.ReadFile(cfg.Containerd.Template)
if err == nil {
logrus.Infof("Using containerd template at %s", cfg.Containerd.Template)
containerdTemplate = string(containerdTemplateBytes)
} else if os.IsNotExist(err) {
containerdTemplate = templates.ContainerdConfigTemplate
} else {
return err
}
parsedTemplate, err := templates.ParseTemplateFromConfig(containerdTemplate, containerdConfig)
if err != nil {
return err
}
return util2.WriteFile(cfg.Containerd.Config, parsedTemplate)
}
// writeContainerdHosts merges registry mirrors/configs, and renders and saves hosts.toml from the filled template
func writeContainerdHosts(cfg *config.Node, containerdConfig templates.ContainerdConfig) error {
mirrorAddr := net.JoinHostPort(spegel.DefaultRegistry.InternalAddress, spegel.DefaultRegistry.RegistryPort)
hosts := getHostConfigs(containerdConfig.PrivateRegistryConfig, containerdConfig.NoDefaultEndpoint, mirrorAddr)
// Clean up previous configuration templates
os.RemoveAll(cfg.Containerd.Registry)
// Write out new templates
for host, config := range hosts {
hostDir := filepath.Join(cfg.Containerd.Registry, host)
hostsFile := filepath.Join(hostDir, "hosts.toml")
hostsTemplate, err := templates.ParseHostsTemplateFromConfig(templates.HostsTomlTemplate, config)
if err != nil {
return err
}
if err := os.MkdirAll(hostDir, 0700); err != nil {
return err
}
if err := util2.WriteFile(hostsFile, hostsTemplate); err != nil {
return err
}
}
return nil
}
// getHostConfigs merges the registry mirrors/configs into HostConfig template structs
func getHostConfigs(registry *registries.Registry, noDefaultEndpoint bool, mirrorAddr string) HostConfigs {
hosts := map[string]templates.HostConfig{}
// create config for default endpoints
for host, config := range registry.Configs {
if c, err := defaultHostConfig(host, mirrorAddr, config); err != nil {
logrus.Errorf("Failed to generate config for registry %s: %v", host, err)
} else {
if host == "*" {
host = "_default"
}
hosts[host] = *c
}
}
// create endpoints for mirrors
for host, mirror := range registry.Mirrors {
// create the default config, if it wasn't explicitly mentioned in the config section
config, ok := hosts[host]
if !ok {
if c, err := defaultHostConfig(host, mirrorAddr, configForHost(registry.Configs, host)); err != nil {
logrus.Errorf("Failed to generate config for registry %s: %v", host, err)
continue
} else {
if noDefaultEndpoint {
c.Default = nil
} else if host == "*" {
c.Default = &templates.RegistryEndpoint{URL: &url.URL{}}
}
config = *c
}
}
// track which endpoints we've already seen to avoid creating duplicates
seenEndpoint := map[string]bool{}
// TODO: rewrites are currently copied from the mirror settings into each endpoint.
// In the future, we should allow for per-endpoint rewrites, instead of expecting
// all mirrors to have the same structure. This will require changes to the registries.yaml
// structure, which is defined in rancher/wharfie.
for i, endpoint := range mirror.Endpoints {
registryName, url, override, err := normalizeEndpointAddress(endpoint, mirrorAddr)
if err != nil {
logrus.Warnf("Ignoring invalid endpoint URL %d=%s for %s: %v", i, endpoint, host, err)
} else if _, ok := seenEndpoint[url.String()]; ok {
logrus.Warnf("Skipping duplicate endpoint URL %d=%s for %s", i, endpoint, host)
} else {
seenEndpoint[url.String()] = true
var rewrites map[string]string
// Do not apply rewrites to the embedded registry endpoint
if url.Host != mirrorAddr {
rewrites = mirror.Rewrites
}
ep := templates.RegistryEndpoint{
Config: configForHost(registry.Configs, registryName),
Rewrites: rewrites,
OverridePath: override,
URL: url,
}
if i+1 == len(mirror.Endpoints) && endpointURLEqual(config.Default, &ep) {
// if the last endpoint is the default endpoint, move it there
config.Default = &ep
} else {
config.Endpoints = append(config.Endpoints, ep)
}
}
}
if host == "*" {
host = "_default"
}
hosts[host] = config
}
// Clean up hosts and default endpoints where resulting config leaves only defaults
for host, config := range hosts {
// if this host has no endpoints and the default has no config, delete this host
if len(config.Endpoints) == 0 && !endpointHasConfig(config.Default) {
delete(hosts, host)
}
}
return hosts
}
// normalizeEndpointAddress normalizes the endpoint address.
// If successful, it returns the registry name, URL, and a bool indicating if the endpoint path should be overridden.
// If unsuccessful, an error is returned.
// Scheme and hostname logic should match containerd:
// https://github.com/containerd/containerd/blob/v1.7.13/remotes/docker/config/hosts.go#L99-L131
func normalizeEndpointAddress(endpoint, mirrorAddr string) (string, *url.URL, bool, error) {
// Ensure that the endpoint address has a scheme so that the URL is parsed properly
if !strings.Contains(endpoint, "://") {
endpoint = "//" + endpoint
}
endpointURL, err := url.Parse(endpoint)
if err != nil {
return "", nil, false, err
}
port := endpointURL.Port()
// set default scheme, if not provided
if endpointURL.Scheme == "" {
// localhost on odd ports defaults to http, unless it's the embedded mirror
if docker.IsLocalhost(endpointURL.Host) && port != "" && port != "443" && endpointURL.Host != mirrorAddr {
endpointURL.Scheme = "http"
} else {
endpointURL.Scheme = "https"
}
}
registry := endpointURL.Host
endpointURL.Host, _ = docker.DefaultHost(registry)
// This is the reverse of the DefaultHost normalization
if endpointURL.Host == "registry-1.docker.io" {
registry = "docker.io"
}
switch endpointURL.Path {
case "", "/", "/v2":
// If the path is empty, /, or /v2, use the default path.
endpointURL.Path = "/v2"
return registry, endpointURL, false, nil
}
return registry, endpointURL, true, nil
}
func defaultHostConfig(host, mirrorAddr string, config registries.RegistryConfig) (*templates.HostConfig, error) {
_, url, _, err := normalizeEndpointAddress(host, mirrorAddr)
if err != nil {
return nil, fmt.Errorf("invalid endpoint URL %s for %s: %v", host, host, err)
}
if host == "*" {
url = nil
}
return &templates.HostConfig{
Program: version.Program,
Default: &templates.RegistryEndpoint{
URL: url,
Config: config,
},
}, nil
}
func configForHost(configs map[string]registries.RegistryConfig, host string) registries.RegistryConfig {
// check for config under modified hostname. If the hostname is unmodified, or there is no config for
// the modified hostname, return the config for the default hostname.
if h, _ := docker.DefaultHost(host); h != host {
if c, ok := configs[h]; ok {
return c
}
}
return configs[host]
}
// endpointURLEqual compares endpoint URL strings
func endpointURLEqual(a, b *templates.RegistryEndpoint) bool {
var au, bu string
if a != nil && a.URL != nil {
au = a.URL.String()
}
if b != nil && b.URL != nil {
bu = b.URL.String()
}
return au == bu
}
func endpointHasConfig(ep *templates.RegistryEndpoint) bool {
if ep != nil {
return ep.OverridePath || ep.Config.Auth != nil || ep.Config.TLS != nil || len(ep.Rewrites) > 0
}
return false
}