1
2
3
4 package hcsoci
5
6 import (
7 "context"
8 "errors"
9 "fmt"
10 "os"
11 "path/filepath"
12 "strconv"
13
14 "github.com/Microsoft/go-winio/pkg/guid"
15 "github.com/Microsoft/hcsshim/internal/cow"
16 "github.com/Microsoft/hcsshim/internal/guestpath"
17 "github.com/Microsoft/hcsshim/internal/hcs"
18 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
19 "github.com/Microsoft/hcsshim/internal/log"
20 "github.com/Microsoft/hcsshim/internal/oci"
21 "github.com/Microsoft/hcsshim/internal/resources"
22 "github.com/Microsoft/hcsshim/internal/schemaversion"
23 "github.com/Microsoft/hcsshim/internal/uvm"
24 specs "github.com/opencontainers/runtime-spec/specs-go"
25 "github.com/sirupsen/logrus"
26 )
27
28 var (
29 lcowRootInUVM = guestpath.LCOWRootPrefixInUVM + "/%s"
30 wcowRootInUVM = guestpath.WCOWRootPrefixInUVM + "/%s"
31 )
32
33
34
35
36
37
38 type CreateOptions struct {
39
40 ID string
41 Owner string
42 Spec *specs.Spec
43 SchemaVersion *hcsschema.Version
44 HostingSystem *uvm.UtilityVM
45 NetworkNamespace string
46
47
48
49
50
51
52 DoNotReleaseResourcesOnFailure bool
53
54
55
56 ScaleCPULimitsToSandbox bool
57 }
58
59
60
61 type createOptionsInternal struct {
62 *CreateOptions
63
64 actualSchemaVersion *hcsschema.Version
65 actualID string
66 actualOwner string
67 actualNetworkNamespace string
68 ccgState *hcsschema.ContainerCredentialGuardState
69
70 windowsAdditionalMounts []hcsschema.MappedDirectory
71 }
72
73 func validateContainerConfig(ctx context.Context, coi *createOptionsInternal) error {
74
75 if coi.Spec.Windows != nil {
76 disableGMSA := oci.ParseAnnotationsDisableGMSA(ctx, coi.Spec)
77 if _, ok := coi.Spec.Windows.CredentialSpec.(string); ok && disableGMSA {
78 return fmt.Errorf("gMSA credentials are disabled: %w", hcs.ErrOperationDenied)
79 }
80 }
81
82 return nil
83 }
84
85 func initializeCreateOptions(ctx context.Context, createOptions *CreateOptions) (*createOptionsInternal, error) {
86 coi := &createOptionsInternal{
87 CreateOptions: createOptions,
88 actualID: createOptions.ID,
89 actualOwner: createOptions.Owner,
90 }
91
92 if coi.Spec == nil {
93 return nil, fmt.Errorf("spec must be supplied")
94 }
95
96
97 if coi.actualID == "" {
98 g, err := guid.NewV4()
99 if err != nil {
100 return nil, err
101 }
102 coi.actualID = g.String()
103 }
104 if coi.actualOwner == "" {
105 coi.actualOwner = filepath.Base(os.Args[0])
106 }
107
108 if coi.HostingSystem != nil {
109
110 coi.actualSchemaVersion = schemaversion.SchemaV21()
111 } else {
112 coi.actualSchemaVersion = schemaversion.DetermineSchemaVersion(coi.SchemaVersion)
113 }
114
115 log.G(ctx).WithFields(logrus.Fields{
116 "options": fmt.Sprintf("%+v", createOptions),
117 "schema": coi.actualSchemaVersion,
118 }).Debug("hcsshim::initializeCreateOptions")
119
120 return coi, nil
121 }
122
123
124
125 func configureSandboxNetwork(ctx context.Context, coi *createOptionsInternal, r *resources.Resources, ct oci.KubernetesContainerType) error {
126 if coi.NetworkNamespace != "" {
127 r.SetNetNS(coi.NetworkNamespace)
128 } else {
129 err := createNetworkNamespace(ctx, coi, r)
130 if err != nil {
131 return err
132 }
133 }
134 coi.actualNetworkNamespace = r.NetNS()
135
136 if coi.HostingSystem != nil {
137
138
139
140 if ct == oci.KubernetesContainerTypeNone || ct == oci.KubernetesContainerTypeSandbox {
141 if err := coi.HostingSystem.ConfigureNetworking(ctx, coi.actualNetworkNamespace); err != nil {
142
143
144 if err == uvm.ErrNoNetworkSetup {
145 if err := coi.HostingSystem.CreateAndAssignNetworkSetup(ctx, "", ""); err != nil {
146 return err
147 }
148 if err := coi.HostingSystem.ConfigureNetworking(ctx, coi.actualNetworkNamespace); err != nil {
149 return err
150 }
151 } else {
152 return err
153 }
154 }
155 r.SetAddedNetNSToVM(true)
156 }
157 }
158
159 return nil
160 }
161
162
163
164
165
166
167
168 func CreateContainer(ctx context.Context, createOptions *CreateOptions) (_ cow.Container, _ *resources.Resources, err error) {
169 coi, err := initializeCreateOptions(ctx, createOptions)
170 if err != nil {
171 return nil, nil, err
172 }
173
174 if err := validateContainerConfig(ctx, coi); err != nil {
175 return nil, nil, fmt.Errorf("container config validation failed: %s", err)
176 }
177
178 r := resources.NewContainerResources(coi.ID)
179 defer func() {
180 if err != nil {
181 if !coi.DoNotReleaseResourcesOnFailure {
182 _ = resources.ReleaseResources(ctx, r, coi.HostingSystem, true)
183 }
184 }
185 }()
186
187 if coi.HostingSystem != nil {
188 if coi.Spec.Linux != nil {
189 r.SetContainerRootInUVM(fmt.Sprintf(lcowRootInUVM, coi.ID))
190 } else {
191 n := coi.HostingSystem.ContainerCounter()
192 r.SetContainerRootInUVM(fmt.Sprintf(wcowRootInUVM, strconv.FormatUint(n, 16)))
193 }
194
195
196
197 driverClosers, err := addSpecGuestDrivers(ctx, coi.HostingSystem, coi.Spec.Annotations)
198 if err != nil {
199 return nil, r, err
200 }
201 r.Add(driverClosers...)
202 }
203
204 ct, _, err := oci.GetSandboxTypeAndID(coi.Spec.Annotations)
205 if err != nil {
206 return nil, r, err
207 }
208 isSandbox := ct == oci.KubernetesContainerTypeSandbox
209
210
211 if coi.Spec.Windows != nil &&
212 coi.Spec.Windows.Network != nil &&
213 schemaversion.IsV21(coi.actualSchemaVersion) {
214 err = configureSandboxNetwork(ctx, coi, r, ct)
215 if err != nil {
216 return nil, r, fmt.Errorf("failure while creating namespace for container: %s", err)
217 }
218 }
219
220 var hcsDocument, gcsDocument interface{}
221 log.G(ctx).Debug("hcsshim::CreateContainer allocating resources")
222 if coi.Spec.Linux != nil {
223 if schemaversion.IsV10(coi.actualSchemaVersion) {
224 return nil, r, errors.New("LCOW v1 not supported")
225 }
226 log.G(ctx).Debug("hcsshim::CreateContainer allocateLinuxResources")
227 err = allocateLinuxResources(ctx, coi, r, isSandbox)
228 if err != nil {
229 log.G(ctx).WithError(err).Debug("failed to allocateLinuxResources")
230 return nil, r, err
231 }
232 gcsDocument, err = createLinuxContainerDocument(ctx, coi, r.ContainerRootInUVM(), r.LcowScratchPath())
233 if err != nil {
234 log.G(ctx).WithError(err).Debug("failed createHCSContainerDocument")
235 return nil, r, err
236 }
237 } else {
238 err = allocateWindowsResources(ctx, coi, r, isSandbox)
239 if err != nil {
240 log.G(ctx).WithError(err).Debug("failed to allocateWindowsResources")
241 return nil, r, err
242 }
243 log.G(ctx).Debug("hcsshim::CreateContainer creating container document")
244 v1, v2, err := createWindowsContainerDocument(ctx, coi)
245 if err != nil {
246 log.G(ctx).WithError(err).Debug("failed createHCSContainerDocument")
247 return nil, r, err
248 }
249
250 if schemaversion.IsV10(coi.actualSchemaVersion) {
251
252 hcsDocument = v1
253 } else if coi.HostingSystem != nil {
254
255 gcsDocument = &hcsschema.HostedSystem{
256 SchemaVersion: schemaversion.SchemaV21(),
257 Container: v2,
258 }
259 } else {
260
261 hcsDocument = &hcsschema.ComputeSystem{
262 Owner: coi.actualOwner,
263 SchemaVersion: schemaversion.SchemaV21(),
264 ShouldTerminateOnLastHandleClosed: true,
265 Container: v2,
266 }
267 }
268 }
269
270 log.G(ctx).Debug("hcsshim::CreateContainer creating compute system")
271 if gcsDocument != nil {
272 c, err := coi.HostingSystem.CreateContainer(ctx, coi.actualID, gcsDocument)
273 if err != nil {
274 return nil, r, err
275 }
276 return c, r, nil
277 }
278
279 system, err := hcs.CreateComputeSystem(ctx, coi.actualID, hcsDocument)
280 if err != nil {
281 return nil, r, err
282 }
283 return system, r, nil
284 }
285
286
287
288 func (coi *createOptionsInternal) isV2Xenon() bool {
289 return schemaversion.IsV21(coi.actualSchemaVersion) && coi.HostingSystem != nil
290 }
291
292
293
294 func (coi *createOptionsInternal) isV1Xenon() bool {
295 return schemaversion.IsV10(coi.actualSchemaVersion) && coi.HostingSystem != nil
296 }
297
298
299
300 func (coi *createOptionsInternal) isV2Argon() bool {
301 return schemaversion.IsV21(coi.actualSchemaVersion) && coi.HostingSystem == nil
302 }
303
304
305
306 func (coi *createOptionsInternal) isV1Argon() bool {
307 return schemaversion.IsV10(coi.actualSchemaVersion) && coi.Spec.Windows.HyperV == nil
308 }
309
310 func (coi *createOptionsInternal) hasWindowsAssignedDevices() bool {
311 return (coi.Spec.Windows != nil) && (coi.Spec.Windows.Devices != nil) &&
312 (len(coi.Spec.Windows.Devices) > 0)
313 }
314
View as plain text