This repository has been archived by the owner on Jan 5, 2023. It is now read-only.
/
service_definition.go
150 lines (121 loc) · 3.84 KB
/
service_definition.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
package userconfig
import (
"crypto/md5"
"encoding/json"
"fmt"
"strings"
)
type ServiceDefinition struct {
// Optional service name
ServiceName ServiceName `json:"name,omitempty"`
// Components
Components ComponentDefinitions `json:"components"`
}
// ParseServiceDefinition tries to parse the v2 service definition.
func ParseServiceDefinition(b []byte) (ServiceDefinition, error) {
var serviceDef ServiceDefinition
if err := json.Unmarshal(b, &serviceDef); err != nil {
if IsSyntax(err) {
if strings.Contains(err.Error(), "$") {
return ServiceDefinition{}, maskf(err, "Cannot parse swarm.json. Maybe not all variables replaced properly.")
}
}
return ServiceDefinition{}, mask(err)
}
return serviceDef, nil
}
func (sd *ServiceDefinition) UnmarshalJSON(data []byte) error {
// We fix the json buffer so CheckForUnknownFields doesn't complain about
// `Components` (with uper N).
data, err := FixJSONFieldNames(data)
if err != nil {
return err
}
if err := CheckForUnknownFields(data, sd); err != nil {
return err
}
// Just unmarshal the given bytes into the service def struct, since there
// were no errors.
var sdc serviceDefCopy
if err := json.Unmarshal(data, &sdc); err != nil {
return mask(err)
}
result := ServiceDefinition(sdc)
*sd = result
return nil
}
type ValidationContext struct {
Org string
Protocols []string
MinScaleSize int
MaxScaleSize int
Placement Placement
MinVolumeSize VolumeSize
MaxVolumeSize VolumeSize
EnableUserMemoryLimit bool // If false, the component definition MUST NOT have a memory-limit configured
MinMemoryLimit ByteSize
MaxMemoryLimit ByteSize
// RestrictedRegistries contains the registry names, where the validator should throw an error, if the repository
// namespace does not contain the Org
RestrictedRegistries []string
}
// validate performs semantic validations of this ServiceDefinition.
// Return the first possible error.
func (sd *ServiceDefinition) Validate(valCtx *ValidationContext) error {
if len(sd.Components) == 0 {
return maskf(InvalidAppDefinitionError, "components must not be empty")
}
if !sd.ServiceName.Empty() {
if err := sd.ServiceName.Validate(); err != nil {
return mask(err)
}
}
if err := sd.Components.validate(valCtx); err != nil {
return mask(err)
}
return nil
}
// HideDefaults uses the given validation context to determine what definition
// details should be hidden. The caller can clean the definition that way to
// not confuse the user with information he has not set by himself.
func (sd *ServiceDefinition) HideDefaults(valCtx *ValidationContext) (*ServiceDefinition, error) {
if valCtx == nil {
return &ServiceDefinition{}, maskf(MissingValidationContextError, "cannot hide defaults")
}
sd.Components = sd.Components.hideDefaults(valCtx)
return sd, nil
}
// SetDefaults sets necessary default values if not given by the user.
func (sd *ServiceDefinition) SetDefaults(valCtx *ValidationContext) error {
if valCtx == nil {
return maskf(MissingValidationContextError, "cannot set defaults")
}
sd.Components.setDefaults(valCtx)
return nil
}
// Name returns the name of the given definition if it exists.
// It is does not exist, it generates an service name.
func (sd *ServiceDefinition) Name() (string, error) {
// Is a name specified?
if !sd.ServiceName.Empty() {
return sd.ServiceName.String(), nil
}
// No name is specified, generate one
if name, err := sd.generateServiceName(); err != nil {
return "", mask(err)
} else {
return name, nil
}
}
// generateServiceName removes any formatting from b and returns the first 4 bytes
// of its MD5 checksum.
func (sd *ServiceDefinition) generateServiceName() (string, error) {
// remove formatting
clean, err := json.Marshal(*sd)
if err != nil {
return "", mask(err)
}
// create hash
s := md5.Sum(clean)
return fmt.Sprintf("%x", s[0:4]), nil
}