/
config.go
160 lines (132 loc) · 4.03 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
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"unicode/utf8"
"github.com/blang/semver"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/validate"
"github.com/sirupsen/logrus"
)
const (
SpecConfig = "config.json"
defaultCwd = "C:\\"
)
func ValidateBundle(logger *logrus.Entry, bundlePath string) (*specs.Spec, error) {
logger.Debug("validating bundle")
if _, err := os.Stat(bundlePath); err != nil {
return nil, &MissingBundleError{BundlePath: bundlePath}
}
configPath := filepath.Join(bundlePath, SpecConfig)
content, err := os.ReadFile(configPath)
if err != nil {
return nil, &MissingBundleConfigError{BundlePath: bundlePath}
}
if !utf8.Valid(content) {
return nil, &BundleConfigInvalidEncodingError{BundlePath: bundlePath}
}
var spec specs.Spec
if err = json.Unmarshal(content, &spec); err != nil {
return nil, &BundleConfigInvalidJSONError{BundlePath: bundlePath, InternalError: err}
}
validator := validate.NewValidator(&spec, bundlePath, true, "windows")
msgs := checkAll(spec, validator)
if len(msgs) != 0 {
for _, m := range msgs {
logger.WithField("bundleConfigError", m).Error(fmt.Sprintf("error in bundle %s", SpecConfig))
}
return nil, &BundleConfigValidationError{BundlePath: bundlePath, ErrorMessages: msgs}
}
return &spec, nil
}
func checkAll(spec specs.Spec, v validate.Validator) []string {
msgs := []string{}
msgs = append(msgs, v.CheckPlatform()...)
msgs = append(msgs, v.CheckMandatoryFields()...)
msgs = append(msgs, checkSemVer(spec.Version)...)
if spec.Root == nil {
msgs = append(msgs, "'root' MUST be set when platform is `windows`")
} else {
if spec.Root.Path == "" {
msgs = append(msgs, "'Spec.Root.Path' should not be empty.")
}
}
return msgs
}
func ValidateProcess(logger *logrus.Entry, processConfig string, overrides *specs.Process) (*specs.Process, error) {
logger.Debug("validating process config")
msgs := []string{}
var spec specs.Process
if processConfig == "" {
spec.Cwd = defaultCwd
} else {
content, err := os.ReadFile(processConfig)
if err != nil {
return nil, &MissingProcessConfigError{ProcessConfig: processConfig}
}
if !utf8.Valid(content) {
return nil, &ProcessConfigInvalidEncodingError{ProcessConfig: processConfig}
}
if err = json.Unmarshal(content, &spec); err != nil {
return nil, &ProcessConfigInvalidJSONError{ProcessConfig: processConfig, InternalError: err}
}
}
if overrides != nil {
if overrides.Cwd != "" {
spec.Cwd = overrides.Cwd
}
if len(overrides.Args) > 0 {
spec.Args = overrides.Args
}
if len(overrides.Env) > 0 {
spec.Env = overrides.Env
}
if overrides.User.Username != "" {
spec.User.Username = overrides.User.Username
}
}
spec.Cwd = toWindowsPath(spec.Cwd)
if !filepath.IsAbs(spec.Cwd) {
msgs = append(msgs, fmt.Sprintf("cwd %q is not an absolute path", spec.Cwd))
}
if len(spec.Args) == 0 {
msgs = append(msgs, "args must not be empty")
}
for _, env := range spec.Env {
if !envValid(env) {
msgs = append(msgs, fmt.Sprintf("env %q should be in the form of 'key=value'.", env))
}
}
if len(msgs) > 0 {
for _, m := range msgs {
logger.WithField("processConfigError", m).Error("error in process config")
}
return nil, &ProcessConfigValidationError{ErrorMessages: msgs}
}
return &spec, nil
}
func envValid(env string) bool {
items := strings.Split(env, "=")
return len(items) >= 2
}
func checkSemVer(version string) []string {
logrus.Debugf("check semver")
parsedVersion, err := semver.Parse(version)
if err != nil {
return []string{fmt.Sprintf("%q is not a valid SemVer: %s", version, err.Error())}
}
if parsedVersion.Major != uint64(specs.VersionMajor) {
return []string{fmt.Sprintf("validate currently only handles version %d.*, but the supplied configuration targets %s", specs.VersionMajor, version)}
}
return []string{}
}
func toWindowsPath(input string) string {
vol := filepath.VolumeName(input)
if vol == "" {
input = filepath.Join("C:", input)
}
return filepath.Clean(input)
}