podman

Форк
0
181 строка · 6.8 Кб
1
package config
2

3
import (
4
	"fmt"
5
	"os"
6
	"strings"
7

8
	"github.com/containers/buildah/docker"
9
	"github.com/containers/image/v5/manifest"
10
	v1 "github.com/opencontainers/image-spec/specs-go/v1"
11
	"github.com/openshift/imagebuilder"
12
)
13

14
// firstStringElseSecondString takes two strings, and returns the first
15
// string if it isn't empty, else the second string
16
func firstStringElseSecondString(first, second string) string {
17
	if first != "" {
18
		return first
19
	}
20
	return second
21
}
22

23
// firstSliceElseSecondSlice takes two string slices, and returns the first
24
// slice of strings if it has contents, else the second slice
25
func firstSliceElseSecondSlice(first, second []string) []string {
26
	if len(first) > 0 {
27
		return append([]string{}, first...)
28
	}
29
	return append([]string{}, second...)
30
}
31

32
// firstSlicePairElseSecondSlicePair takes two pairs of string slices, and
33
// returns the first pair of slices if either has contents, else the second
34
// pair
35
func firstSlicePairElseSecondSlicePair(firstA, firstB, secondA, secondB []string) ([]string, []string) {
36
	if len(firstA) > 0 || len(firstB) > 0 {
37
		return append([]string{}, firstA...), append([]string{}, firstB...)
38
	}
39
	return append([]string{}, secondA...), append([]string{}, secondB...)
40
}
41

42
// mergeEnv combines variables from a and b into a single environment slice. if
43
// a and b both provide values for the same variable, the value from b is
44
// preferred
45
func mergeEnv(a, b []string) []string {
46
	index := make(map[string]int)
47
	results := make([]string, 0, len(a)+len(b))
48
	for _, kv := range append(append([]string{}, a...), b...) {
49
		k, _, specifiesValue := strings.Cut(kv, "=")
50
		if !specifiesValue {
51
			if value, ok := os.LookupEnv(kv); ok {
52
				kv = kv + "=" + value
53
			} else {
54
				kv = kv + "="
55
			}
56
		}
57
		if i, seen := index[k]; seen {
58
			results[i] = kv
59
		} else {
60
			index[k] = len(results)
61
			results = append(results, kv)
62
		}
63
	}
64
	return results
65
}
66

67
// Override takes a buildah docker config and an OCI ImageConfig, and applies a
68
// mixture of a slice of Dockerfile-style instructions and fields from a config
69
// blob to them both
70
func Override(dconfig *docker.Config, oconfig *v1.ImageConfig, overrideChanges []string, overrideConfig *manifest.Schema2Config) error {
71
	if len(overrideChanges) > 0 {
72
		if overrideConfig == nil {
73
			overrideConfig = &manifest.Schema2Config{}
74
		}
75
		// Parse the set of changes as we would a Dockerfile.
76
		changes := strings.Join(overrideChanges, "\n")
77
		parsed, err := imagebuilder.ParseDockerfile(strings.NewReader(changes))
78
		if err != nil {
79
			return fmt.Errorf("parsing change set %+v: %w", changes, err)
80
		}
81
		// Create a dummy builder object to process configuration-related
82
		// instructions.
83
		subBuilder := imagebuilder.NewBuilder(nil)
84
		// Convert the incoming data into an initial RunConfig.
85
		subBuilder.RunConfig = *GoDockerclientConfigFromSchema2Config(overrideConfig)
86
		// Process the change instructions one by one.
87
		for _, node := range parsed.Children {
88
			var step imagebuilder.Step
89
			if err := step.Resolve(node); err != nil {
90
				return fmt.Errorf("resolving change %q: %w", node.Original, err)
91
			}
92
			if err := subBuilder.Run(&step, &configOnlyExecutor{}, true); err != nil {
93
				return fmt.Errorf("processing change %q: %w", node.Original, err)
94
			}
95
		}
96
		// Pull settings out of the dummy builder's RunConfig.
97
		overrideConfig = Schema2ConfigFromGoDockerclientConfig(&subBuilder.RunConfig)
98
	}
99
	if overrideConfig != nil {
100
		// Apply changes from a possibly-provided possibly-changed config struct.
101
		dconfig.Hostname = firstStringElseSecondString(overrideConfig.Hostname, dconfig.Hostname)
102
		dconfig.Domainname = firstStringElseSecondString(overrideConfig.Domainname, dconfig.Domainname)
103
		dconfig.User = firstStringElseSecondString(overrideConfig.User, dconfig.User)
104
		oconfig.User = firstStringElseSecondString(overrideConfig.User, oconfig.User)
105
		dconfig.AttachStdin = overrideConfig.AttachStdin
106
		dconfig.AttachStdout = overrideConfig.AttachStdout
107
		dconfig.AttachStderr = overrideConfig.AttachStderr
108
		if len(overrideConfig.ExposedPorts) > 0 {
109
			dexposedPorts := make(map[docker.Port]struct{})
110
			oexposedPorts := make(map[string]struct{})
111
			for port := range dconfig.ExposedPorts {
112
				dexposedPorts[port] = struct{}{}
113
			}
114
			for port := range overrideConfig.ExposedPorts {
115
				dexposedPorts[docker.Port(port)] = struct{}{}
116
			}
117
			for port := range oconfig.ExposedPorts {
118
				oexposedPorts[port] = struct{}{}
119
			}
120
			for port := range overrideConfig.ExposedPorts {
121
				oexposedPorts[string(port)] = struct{}{}
122
			}
123
			dconfig.ExposedPorts = dexposedPorts
124
			oconfig.ExposedPorts = oexposedPorts
125
		}
126
		dconfig.Tty = overrideConfig.Tty
127
		dconfig.OpenStdin = overrideConfig.OpenStdin
128
		dconfig.StdinOnce = overrideConfig.StdinOnce
129
		if len(overrideConfig.Env) > 0 {
130
			dconfig.Env = mergeEnv(dconfig.Env, overrideConfig.Env)
131
			oconfig.Env = mergeEnv(oconfig.Env, overrideConfig.Env)
132
		}
133
		dconfig.Entrypoint, dconfig.Cmd = firstSlicePairElseSecondSlicePair(overrideConfig.Entrypoint, overrideConfig.Cmd, dconfig.Entrypoint, dconfig.Cmd)
134
		oconfig.Entrypoint, oconfig.Cmd = firstSlicePairElseSecondSlicePair(overrideConfig.Entrypoint, overrideConfig.Cmd, oconfig.Entrypoint, oconfig.Cmd)
135
		if overrideConfig.Healthcheck != nil {
136
			dconfig.Healthcheck = &docker.HealthConfig{
137
				Test:        append([]string{}, overrideConfig.Healthcheck.Test...),
138
				Interval:    overrideConfig.Healthcheck.Interval,
139
				Timeout:     overrideConfig.Healthcheck.Timeout,
140
				StartPeriod: overrideConfig.Healthcheck.StartPeriod,
141
				Retries:     overrideConfig.Healthcheck.Retries,
142
			}
143
		}
144
		dconfig.ArgsEscaped = overrideConfig.ArgsEscaped
145
		dconfig.Image = firstStringElseSecondString(overrideConfig.Image, dconfig.Image)
146
		if len(overrideConfig.Volumes) > 0 {
147
			if dconfig.Volumes == nil {
148
				dconfig.Volumes = make(map[string]struct{})
149
			}
150
			if oconfig.Volumes == nil {
151
				oconfig.Volumes = make(map[string]struct{})
152
			}
153
			for volume := range overrideConfig.Volumes {
154
				dconfig.Volumes[volume] = struct{}{}
155
				oconfig.Volumes[volume] = struct{}{}
156
			}
157
		}
158
		dconfig.WorkingDir = firstStringElseSecondString(overrideConfig.WorkingDir, dconfig.WorkingDir)
159
		oconfig.WorkingDir = firstStringElseSecondString(overrideConfig.WorkingDir, oconfig.WorkingDir)
160
		dconfig.NetworkDisabled = overrideConfig.NetworkDisabled
161
		dconfig.MacAddress = overrideConfig.MacAddress
162
		dconfig.OnBuild = overrideConfig.OnBuild
163
		if len(overrideConfig.Labels) > 0 {
164
			if dconfig.Labels == nil {
165
				dconfig.Labels = make(map[string]string)
166
			}
167
			if oconfig.Labels == nil {
168
				oconfig.Labels = make(map[string]string)
169
			}
170
			for k, v := range overrideConfig.Labels {
171
				dconfig.Labels[k] = v
172
				oconfig.Labels[k] = v
173
			}
174
		}
175
		dconfig.StopSignal = overrideConfig.StopSignal
176
		oconfig.StopSignal = overrideConfig.StopSignal
177
		dconfig.StopTimeout = overrideConfig.StopTimeout
178
		dconfig.Shell = firstSliceElseSecondSlice(overrideConfig.Shell, dconfig.Shell)
179
	}
180
	return nil
181
}
182

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.