• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes / kompose / 6696194161

30 Oct 2023 05:02PM UTC coverage: 54.96%. Remained the same
6696194161

push

github

web-flow
chore(deps)(deps): bump golang.org/x/tools from 0.13.0 to 0.14.0 (#1723)

Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.13.0 to 0.14.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

2205 of 4012 relevant lines covered (54.96%)

7.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.97
/pkg/transformer/kubernetes/kubernetes.go
1
/*
2
Copyright 2017 The Kubernetes Authors All rights reserved.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package kubernetes
18

19
import (
20
        "encoding/base64"
21
        "fmt"
22
        "os"
23
        "os/exec"
24
        "path"
25
        "path/filepath"
26
        "reflect"
27
        "regexp"
28
        "sort"
29
        "strconv"
30
        "strings"
31

32
        "github.com/compose-spec/compose-go/types"
33
        "github.com/fatih/structs"
34
        "github.com/kubernetes/kompose/pkg/kobject"
35
        "github.com/kubernetes/kompose/pkg/loader/compose"
36
        "github.com/kubernetes/kompose/pkg/transformer"
37
        "github.com/mattn/go-shellwords"
38
        deployapi "github.com/openshift/api/apps/v1"
39
        buildapi "github.com/openshift/api/build/v1"
40
        "github.com/pkg/errors"
41
        log "github.com/sirupsen/logrus"
42
        "github.com/spf13/cast"
43
        "golang.org/x/tools/godoc/util"
44
        appsv1 "k8s.io/api/apps/v1"
45
        api "k8s.io/api/core/v1"
46
        networkingv1 "k8s.io/api/networking/v1"
47
        "k8s.io/apimachinery/pkg/api/resource"
48
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
49
        "k8s.io/apimachinery/pkg/runtime"
50
        "k8s.io/apimachinery/pkg/util/intstr"
51
)
52

53
// Kubernetes implements Transformer interface and represents Kubernetes transformer
54
type Kubernetes struct {
55
        // the user provided options from the command line
56
        Opt kobject.ConvertOptions
57
}
58

59
// PVCRequestSize (Persistent Volume Claim) has default size
60
const PVCRequestSize = "100Mi"
61

62
// ValidVolumeSet has the different types of valid volumes
63
var ValidVolumeSet = map[string]struct{}{"emptyDir": {}, "hostPath": {}, "configMap": {}, "persistentVolumeClaim": {}}
64

65
const (
66
        // DeploymentController is controller type for Deployment
67
        DeploymentController = "deployment"
68
        // DaemonSetController is controller type for DaemonSet
69
        DaemonSetController = "daemonset"
70
        // StatefulStateController is controller type for StatefulSet
71
        StatefulStateController = "statefulset"
72
)
73

74
// CheckUnsupportedKey checks if given komposeObject contains
75
// keys that are not supported by this transformer.
76
// list of all unsupported keys are stored in unsupportedKey variable
77
// returns list of TODO: ....
78
func (k *Kubernetes) CheckUnsupportedKey(komposeObject *kobject.KomposeObject, unsupportedKey map[string]bool) []string {
×
79
        // collect all keys found in project
×
80
        var keysFound []string
×
81

×
82
        for _, serviceConfig := range komposeObject.ServiceConfigs {
×
83
                // this reflection is used in check for empty arrays
×
84
                val := reflect.ValueOf(serviceConfig)
×
85
                s := structs.New(serviceConfig)
×
86

×
87
                for _, f := range s.Fields() {
×
88
                        // Check if given key is among unsupported keys, and skip it if we already saw this key
×
89
                        if alreadySaw, ok := unsupportedKey[f.Name()]; ok && !alreadySaw {
×
90
                                if f.IsExported() && !f.IsZero() {
×
91
                                        // IsZero returns false for empty array/slice ([])
×
92
                                        // this check if field is Slice, and then it checks its size
×
93
                                        if field := val.FieldByName(f.Name()); field.Kind() == reflect.Slice {
×
94
                                                if field.Len() == 0 {
×
95
                                                        // array is empty it doesn't matter if it is in unsupportedKey or not
×
96
                                                        continue
×
97
                                                }
98
                                        }
99
                                        //get tag from kobject service configure
100
                                        tag := f.Tag(komposeObject.LoadedFrom)
×
101
                                        keysFound = append(keysFound, tag)
×
102
                                        unsupportedKey[f.Name()] = true
×
103
                                }
104
                        }
105
                }
106
        }
107
        return keysFound
×
108
}
109

110
// InitPodSpec creates the pod specification
111
func (k *Kubernetes) InitPodSpec(name string, image string, pullSecret string) api.PodSpec {
27✔
112
        if image == "" {
27✔
113
                image = name
×
114
        }
×
115
        pod := api.PodSpec{
27✔
116
                Containers: []api.Container{
27✔
117
                        {
27✔
118
                                Name:  name,
27✔
119
                                Image: image,
27✔
120
                        },
27✔
121
                },
27✔
122
        }
27✔
123
        if pullSecret != "" {
30✔
124
                pod.ImagePullSecrets = []api.LocalObjectReference{
3✔
125
                        {
3✔
126
                                Name: pullSecret,
3✔
127
                        },
3✔
128
                }
3✔
129
        }
3✔
130
        return pod
27✔
131
}
132

133
// InitPodSpecWithConfigMap creates the pod specification
134
func (k *Kubernetes) InitPodSpecWithConfigMap(name string, image string, service kobject.ServiceConfig) api.PodSpec {
8✔
135
        var volumeMounts []api.VolumeMount
8✔
136
        var volumes []api.Volume
8✔
137

8✔
138
        for _, value := range service.Configs {
16✔
139
                cmVolName := FormatFileName(value.Source)
8✔
140
                target := value.Target
8✔
141
                if target == "" {
8✔
142
                        // short syntax, = /<source>
×
143
                        target = "/" + value.Source
×
144
                }
×
145
                subPath := filepath.Base(target)
8✔
146

8✔
147
                volSource := api.ConfigMapVolumeSource{}
8✔
148
                volSource.Name = cmVolName
8✔
149
                key, err := service.GetConfigMapKeyFromMeta(value.Source)
8✔
150
                if err != nil {
8✔
151
                        log.Warnf("cannot parse config %s , %s", value.Source, err.Error())
×
152
                        // mostly it's external
×
153
                        continue
×
154
                }
155
                volSource.Items = []api.KeyToPath{{
8✔
156
                        Key:  key,
8✔
157
                        Path: subPath,
8✔
158
                }}
8✔
159

8✔
160
                if value.Mode != nil {
8✔
161
                        tmpMode := int32(*value.Mode)
×
162
                        volSource.DefaultMode = &tmpMode
×
163
                }
×
164

165
                cmVol := api.Volume{
8✔
166
                        Name:         cmVolName,
8✔
167
                        VolumeSource: api.VolumeSource{ConfigMap: &volSource},
8✔
168
                }
8✔
169

8✔
170
                volumeMounts = append(volumeMounts,
8✔
171
                        api.VolumeMount{
8✔
172
                                Name:      cmVolName,
8✔
173
                                MountPath: target,
8✔
174
                                SubPath:   subPath,
8✔
175
                        })
8✔
176
                volumes = append(volumes, cmVol)
8✔
177
        }
178

179
        pod := api.PodSpec{
8✔
180
                Containers: []api.Container{
8✔
181
                        {
8✔
182
                                Name:         name,
8✔
183
                                Image:        image,
8✔
184
                                VolumeMounts: volumeMounts,
8✔
185
                        },
8✔
186
                },
8✔
187
                Volumes: volumes,
8✔
188
        }
8✔
189

8✔
190
        if service.ImagePullSecret != "" {
16✔
191
                pod.ImagePullSecrets = []api.LocalObjectReference{
8✔
192
                        {
8✔
193
                                Name: service.ImagePullSecret,
8✔
194
                        },
8✔
195
                }
8✔
196
        }
8✔
197
        return pod
8✔
198
}
199

200
// InitSvc initializes Kubernetes Service object
201
// The created service name will = ServiceConfig.Name, but the selector may be not.
202
// If this service is grouped, the selector may be another name = name
203
func (k *Kubernetes) InitSvc(name string, service kobject.ServiceConfig) *api.Service {
31✔
204
        svc := &api.Service{
31✔
205
                TypeMeta: metav1.TypeMeta{
31✔
206
                        Kind:       "Service",
31✔
207
                        APIVersion: "v1",
31✔
208
                },
31✔
209
                ObjectMeta: metav1.ObjectMeta{
31✔
210
                        Name:   service.Name,
31✔
211
                        Labels: transformer.ConfigLabels(name),
31✔
212
                },
31✔
213
                // The selector uses the service.Name, which must be consistent with workloads label
31✔
214
                Spec: api.ServiceSpec{
31✔
215
                        Selector: transformer.ConfigLabels(name),
31✔
216
                },
31✔
217
        }
31✔
218
        return svc
31✔
219
}
31✔
220

221
// InitConfigMapForEnv initializes a ConfigMap object
222
func (k *Kubernetes) InitConfigMapForEnv(name string, opt kobject.ConvertOptions, envFile string) *api.ConfigMap {
×
223
        envs, err := GetEnvsFromFile(envFile)
×
224
        if err != nil {
×
225
                log.Fatalf("Unable to retrieve env file: %s", err)
×
226
        }
×
227

228
        // Remove root pathing
229
        // replace all other slashes / periods
230
        envName := FormatEnvName(envFile)
×
231

×
232
        // In order to differentiate files, we append to the name and remove '.env' if applicable from the file name
×
233
        configMap := &api.ConfigMap{
×
234
                TypeMeta: metav1.TypeMeta{
×
235
                        Kind:       "ConfigMap",
×
236
                        APIVersion: "v1",
×
237
                },
×
238
                ObjectMeta: metav1.ObjectMeta{
×
239
                        Name:   envName,
×
240
                        Labels: transformer.ConfigLabels(name + "-" + envName),
×
241
                },
×
242
                Data: envs,
×
243
        }
×
244

×
245
        return configMap
×
246
}
247

248
// IntiConfigMapFromFileOrDir will create a configmap from dir or file
249
// usage:
250
//  1. volume
251
func (k *Kubernetes) IntiConfigMapFromFileOrDir(name, cmName, filePath string, service kobject.ServiceConfig) (*api.ConfigMap, error) {
×
252
        configMap := &api.ConfigMap{
×
253
                TypeMeta: metav1.TypeMeta{
×
254
                        Kind:       "ConfigMap",
×
255
                        APIVersion: "v1",
×
256
                },
×
257
                ObjectMeta: metav1.ObjectMeta{
×
258
                        Name:   cmName,
×
259
                        Labels: transformer.ConfigLabels(name),
×
260
                },
×
261
        }
×
262
        dataMap := make(map[string]string)
×
263

×
264
        fi, err := os.Stat(filePath)
×
265
        if err != nil {
×
266
                return nil, err
×
267
        }
×
268

269
        switch mode := fi.Mode(); {
×
270
        case mode.IsDir():
×
271
                files, err := os.ReadDir(filePath)
×
272
                if err != nil {
×
273
                        return nil, err
×
274
                }
×
275

276
                for _, file := range files {
×
277
                        if !file.IsDir() {
×
278
                                log.Debugf("Read file to ConfigMap: %s", file.Name())
×
279
                                data, err := GetContentFromFile(filePath + "/" + file.Name())
×
280
                                if err != nil {
×
281
                                        return nil, err
×
282
                                }
×
283
                                dataMap[file.Name()] = data
×
284
                        }
285
                }
286
                initConfigMapData(configMap, dataMap)
×
287

288
        case mode.IsRegular():
×
289
                // do file stuff
×
290
                configMap = k.InitConfigMapFromFile(name, service, filePath)
×
291
                configMap.Name = cmName
×
292
                configMap.Annotations = map[string]string{
×
293
                        "use-subpath": "true",
×
294
                }
×
295
        }
296

297
        return configMap, nil
×
298
}
299

300
// useSubPathMount check if a configmap should be mounted as subpath
301
// in this situation, this configmap will only contains 1 key in data
302
func useSubPathMount(cm *api.ConfigMap) bool {
×
303
        if cm.Annotations == nil {
×
304
                return false
×
305
        }
×
306
        if cm.Annotations["use-subpath"] != "true" {
×
307
                return false
×
308
        }
×
309
        return true
×
310
}
311

312
func initConfigMapData(configMap *api.ConfigMap, data map[string]string) {
16✔
313
        stringData := map[string]string{}
16✔
314
        binData := map[string][]byte{}
16✔
315

16✔
316
        for k, v := range data {
32✔
317
                isText := util.IsText([]byte(v))
16✔
318
                if isText {
32✔
319
                        stringData[k] = v
16✔
320
                } else {
16✔
321
                        binData[k] = []byte(base64.StdEncoding.EncodeToString([]byte(v)))
×
322
                }
×
323
        }
324

325
        configMap.Data = stringData
16✔
326
        configMap.BinaryData = binData
16✔
327
}
328

329
// InitConfigMapFromFile initializes a ConfigMap object
330
func (k *Kubernetes) InitConfigMapFromFile(name string, service kobject.ServiceConfig, fileName string) *api.ConfigMap {
16✔
331
        content, err := GetContentFromFile(fileName)
16✔
332
        if err != nil {
16✔
333
                log.Fatalf("Unable to retrieve file: %s", err)
×
334
        }
×
335

336
        configMapName := ""
16✔
337
        for key, tmpConfig := range service.ConfigsMetaData {
32✔
338
                if tmpConfig.File == fileName {
32✔
339
                        configMapName = key
16✔
340
                }
16✔
341
        }
342
        configMap := &api.ConfigMap{
16✔
343
                TypeMeta: metav1.TypeMeta{
16✔
344
                        Kind:       "ConfigMap",
16✔
345
                        APIVersion: "v1",
16✔
346
                },
16✔
347
                ObjectMeta: metav1.ObjectMeta{
16✔
348
                        Name:   FormatFileName(configMapName),
16✔
349
                        Labels: transformer.ConfigLabels(name),
16✔
350
                },
16✔
351
        }
16✔
352

16✔
353
        data := map[string]string{filepath.Base(fileName): content}
16✔
354
        initConfigMapData(configMap, data)
16✔
355
        return configMap
16✔
356
}
357

358
// InitD initializes Kubernetes Deployment object
359
func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas int) *appsv1.Deployment {
27✔
360
        var podSpec api.PodSpec
27✔
361
        if len(service.Configs) > 0 {
33✔
362
                podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service)
6✔
363
        } else {
27✔
364
                podSpec = k.InitPodSpec(name, service.Image, service.ImagePullSecret)
21✔
365
        }
21✔
366

367
        rp := int32(replicas)
27✔
368

27✔
369
        dc := &appsv1.Deployment{
27✔
370
                TypeMeta: metav1.TypeMeta{
27✔
371
                        Kind:       "Deployment",
27✔
372
                        APIVersion: "apps/v1",
27✔
373
                },
27✔
374
                ObjectMeta: metav1.ObjectMeta{
27✔
375
                        Name:   name,
27✔
376
                        Labels: transformer.ConfigAllLabels(name, &service),
27✔
377
                },
27✔
378
                Spec: appsv1.DeploymentSpec{
27✔
379
                        Replicas: &rp,
27✔
380
                        Selector: &metav1.LabelSelector{
27✔
381
                                MatchLabels: transformer.ConfigLabels(name),
27✔
382
                        },
27✔
383
                        Template: api.PodTemplateSpec{
27✔
384
                                ObjectMeta: metav1.ObjectMeta{
27✔
385
                                        //Labels: transformer.ConfigLabels(name),
27✔
386
                                        Annotations: transformer.ConfigAnnotations(service),
27✔
387
                                },
27✔
388
                                Spec: podSpec,
27✔
389
                        },
27✔
390
                },
27✔
391
        }
27✔
392
        dc.Spec.Template.Labels = transformer.ConfigLabels(name)
27✔
393

27✔
394
        update := service.GetKubernetesUpdateStrategy()
27✔
395
        if update != nil {
27✔
396
                dc.Spec.Strategy = appsv1.DeploymentStrategy{
×
397
                        Type:          appsv1.RollingUpdateDeploymentStrategyType,
×
398
                        RollingUpdate: update,
×
399
                }
×
400
                ms := ""
×
401
                if update.MaxSurge != nil {
×
402
                        ms = update.MaxSurge.String()
×
403
                }
×
404
                mu := ""
×
405
                if update.MaxUnavailable != nil {
×
406
                        mu = update.MaxUnavailable.String()
×
407
                }
×
408
                log.Debugf("Set deployment '%s' rolling update: MaxSurge: %s, MaxUnavailable: %s", name, ms, mu)
×
409
        }
410

411
        return dc
27✔
412
}
413

414
// InitDS initializes Kubernetes DaemonSet object
415
func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *appsv1.DaemonSet {
3✔
416
        ds := &appsv1.DaemonSet{
3✔
417
                TypeMeta: metav1.TypeMeta{
3✔
418
                        Kind:       "DaemonSet",
3✔
419
                        APIVersion: "apps/v1",
3✔
420
                },
3✔
421
                ObjectMeta: metav1.ObjectMeta{
3✔
422
                        Name:   name,
3✔
423
                        Labels: transformer.ConfigAllLabels(name, &service),
3✔
424
                },
3✔
425
                Spec: appsv1.DaemonSetSpec{
3✔
426
                        Selector: &metav1.LabelSelector{
3✔
427
                                MatchLabels: transformer.ConfigLabels(name),
3✔
428
                        },
3✔
429
                        Template: api.PodTemplateSpec{
3✔
430
                                Spec: k.InitPodSpec(name, service.Image, service.ImagePullSecret),
3✔
431
                        },
3✔
432
                },
3✔
433
        }
3✔
434
        return ds
3✔
435
}
3✔
436

437
// InitSS method initialize a stateful set
438
func (k *Kubernetes) InitSS(name string, service kobject.ServiceConfig, replicas int) *appsv1.StatefulSet {
2✔
439
        var podSpec api.PodSpec
2✔
440
        if len(service.Configs) > 0 {
4✔
441
                podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service)
2✔
442
        } else {
2✔
443
                podSpec = k.InitPodSpec(name, service.Image, service.ImagePullSecret)
×
444
        }
×
445
        rp := int32(replicas)
2✔
446
        ds := &appsv1.StatefulSet{
2✔
447
                TypeMeta: metav1.TypeMeta{
2✔
448
                        Kind:       "StatefulSet",
2✔
449
                        APIVersion: "apps/v1",
2✔
450
                },
2✔
451
                ObjectMeta: metav1.ObjectMeta{
2✔
452
                        Name:   name,
2✔
453
                        Labels: transformer.ConfigAllLabels(name, &service),
2✔
454
                },
2✔
455
                Spec: appsv1.StatefulSetSpec{
2✔
456
                        Replicas: &rp,
2✔
457
                        Template: api.PodTemplateSpec{
2✔
458
                                Spec: podSpec,
2✔
459
                        },
2✔
460
                        Selector: &metav1.LabelSelector{
2✔
461
                                MatchLabels: transformer.ConfigLabels(name),
2✔
462
                        },
2✔
463
                        ServiceName: service.Name,
2✔
464
                },
2✔
465
        }
2✔
466
        return ds
2✔
467
}
468

469
func (k *Kubernetes) initIngress(name string, service kobject.ServiceConfig, port int32) *networkingv1.Ingress {
2✔
470
        hosts := regexp.MustCompile("[ ,]*,[ ,]*").Split(service.ExposeService, -1)
2✔
471

2✔
472
        ingress := &networkingv1.Ingress{
2✔
473
                TypeMeta: metav1.TypeMeta{
2✔
474
                        Kind:       "Ingress",
2✔
475
                        APIVersion: "networking.k8s.io/v1",
2✔
476
                },
2✔
477
                ObjectMeta: metav1.ObjectMeta{
2✔
478
                        Name:        name,
2✔
479
                        Labels:      transformer.ConfigLabels(name),
2✔
480
                        Annotations: transformer.ConfigAnnotations(service),
2✔
481
                },
2✔
482
                Spec: networkingv1.IngressSpec{
2✔
483
                        Rules: make([]networkingv1.IngressRule, len(hosts)),
2✔
484
                },
2✔
485
        }
2✔
486
        tlsHosts := make([]string, len(hosts))
2✔
487
        pathType := networkingv1.PathTypePrefix
2✔
488
        for i, host := range hosts {
4✔
489
                host, p := transformer.ParseIngressPath(host)
2✔
490
                if p == "" {
4✔
491
                        p = "/"
2✔
492
                }
2✔
493
                ingress.Spec.Rules[i] = networkingv1.IngressRule{
2✔
494
                        IngressRuleValue: networkingv1.IngressRuleValue{
2✔
495
                                HTTP: &networkingv1.HTTPIngressRuleValue{
2✔
496
                                        Paths: []networkingv1.HTTPIngressPath{
2✔
497
                                                {
2✔
498
                                                        Path:     p,
2✔
499
                                                        PathType: &pathType,
2✔
500
                                                        Backend: networkingv1.IngressBackend{
2✔
501
                                                                Service: &networkingv1.IngressServiceBackend{
2✔
502
                                                                        Name: name,
2✔
503
                                                                        Port: networkingv1.ServiceBackendPort{
2✔
504
                                                                                Number: port,
2✔
505
                                                                        },
2✔
506
                                                                },
2✔
507
                                                        },
2✔
508
                                                },
2✔
509
                                        },
2✔
510
                                },
2✔
511
                        },
2✔
512
                }
2✔
513
                if host != "true" {
3✔
514
                        ingress.Spec.Rules[i].Host = host
1✔
515
                        tlsHosts[i] = host
1✔
516
                }
1✔
517
        }
518
        if service.ExposeServiceTLS != "" {
2✔
519
                if service.ExposeServiceTLS != "true" {
×
520
                        ingress.Spec.TLS = []networkingv1.IngressTLS{
×
521
                                {
×
522
                                        Hosts:      tlsHosts,
×
523
                                        SecretName: service.ExposeServiceTLS,
×
524
                                },
×
525
                        }
×
526
                } else {
×
527
                        ingress.Spec.TLS = []networkingv1.IngressTLS{
×
528
                                {
×
529
                                        Hosts: tlsHosts,
×
530
                                },
×
531
                        }
×
532
                }
×
533
        }
534

535
        if service.ExposeServiceIngressClassName != "" {
2✔
536
                ingress.Spec.IngressClassName = &service.ExposeServiceIngressClassName
×
537
        }
×
538

539
        return ingress
2✔
540
}
541

542
// CreateSecrets create secrets
543
func (k *Kubernetes) CreateSecrets(komposeObject kobject.KomposeObject) ([]*api.Secret, error) {
×
544
        var objects []*api.Secret
×
545
        for name, config := range komposeObject.Secrets {
×
546
                if config.File != "" {
×
547
                        dataString, err := GetContentFromFile(config.File)
×
548
                        if err != nil {
×
549
                                log.Fatal("unable to read secret from file: ", config.File)
×
550
                                return nil, err
×
551
                        }
×
552
                        data := []byte(dataString)
×
553
                        secret := &api.Secret{
×
554
                                TypeMeta: metav1.TypeMeta{
×
555
                                        Kind:       "Secret",
×
556
                                        APIVersion: "v1",
×
557
                                },
×
558
                                ObjectMeta: metav1.ObjectMeta{
×
559
                                        Name:   FormatResourceName(name),
×
560
                                        Labels: transformer.ConfigLabels(name),
×
561
                                },
×
562
                                Type: api.SecretTypeOpaque,
×
563
                                Data: map[string][]byte{name: data},
×
564
                        }
×
565
                        objects = append(objects, secret)
×
566
                } else {
×
567
                        log.Warnf("External secrets %s is not currently supported - ignoring", name)
×
568
                }
×
569
        }
570
        return objects, nil
×
571
}
572

573
// CreatePVC initializes PersistentVolumeClaim
574
func (k *Kubernetes) CreatePVC(name string, mode string, size string, selectorValue string, storageClassName string) (*api.PersistentVolumeClaim, error) {
22✔
575
        volSize, err := resource.ParseQuantity(size)
22✔
576
        if err != nil {
22✔
577
                return nil, errors.Wrap(err, "resource.ParseQuantity failed, Error parsing size")
×
578
        }
×
579

580
        pvc := &api.PersistentVolumeClaim{
22✔
581
                TypeMeta: metav1.TypeMeta{
22✔
582
                        Kind:       "PersistentVolumeClaim",
22✔
583
                        APIVersion: "v1",
22✔
584
                },
22✔
585
                ObjectMeta: metav1.ObjectMeta{
22✔
586
                        Name:   name,
22✔
587
                        Labels: transformer.ConfigLabels(name),
22✔
588
                },
22✔
589
                Spec: api.PersistentVolumeClaimSpec{
22✔
590
                        Resources: api.ResourceRequirements{
22✔
591
                                Requests: api.ResourceList{
22✔
592
                                        api.ResourceStorage: volSize,
22✔
593
                                },
22✔
594
                        },
22✔
595
                },
22✔
596
        }
22✔
597

22✔
598
        if len(selectorValue) > 0 {
22✔
599
                pvc.Spec.Selector = &metav1.LabelSelector{
×
600
                        MatchLabels: transformer.ConfigLabels(selectorValue),
×
601
                }
×
602
        }
×
603

604
        if mode == "ro" {
22✔
605
                pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadOnlyMany}
×
606
        } else {
22✔
607
                pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce}
22✔
608
        }
22✔
609

610
        if len(storageClassName) > 0 {
23✔
611
                pvc.Spec.StorageClassName = &storageClassName
1✔
612
        }
1✔
613

614
        return pvc, nil
22✔
615
}
616

617
// ConfigPorts configures the container ports.
618
func ConfigPorts(service kobject.ServiceConfig) []api.ContainerPort {
48✔
619
        var ports []api.ContainerPort
48✔
620
        exist := map[string]bool{}
48✔
621
        for _, port := range service.Port {
122✔
622
                if exist[port.ID()] {
74✔
623
                        continue
×
624
                }
625
                containerPort := api.ContainerPort{
74✔
626
                        ContainerPort: port.ContainerPort,
74✔
627
                        HostIP:        port.HostIP,
74✔
628
                        HostPort:      port.HostPort,
74✔
629
                        Protocol:      api.Protocol(port.Protocol),
74✔
630
                }
74✔
631
                ports = append(ports, containerPort)
74✔
632
                exist[port.ID()] = true
74✔
633
        }
634

635
        return ports
48✔
636
}
637

638
// ConfigLBServicePorts method configure the ports of the k8s Load Balancer Service
639
func (k *Kubernetes) ConfigLBServicePorts(service kobject.ServiceConfig) ([]api.ServicePort, []api.ServicePort) {
1✔
640
        var tcpPorts []api.ServicePort
1✔
641
        var udpPorts []api.ServicePort
1✔
642
        for _, port := range service.Port {
2✔
643
                if port.HostPort == 0 {
1✔
644
                        port.HostPort = port.ContainerPort
×
645
                }
×
646
                var targetPort intstr.IntOrString
1✔
647
                targetPort.IntVal = port.ContainerPort
1✔
648
                targetPort.StrVal = strconv.Itoa(int(port.ContainerPort))
1✔
649

1✔
650
                servicePort := api.ServicePort{
1✔
651
                        Name:       strconv.Itoa(int(port.HostPort)),
1✔
652
                        Port:       port.HostPort,
1✔
653
                        TargetPort: targetPort,
1✔
654
                }
1✔
655

1✔
656
                if protocol := api.Protocol(port.Protocol); protocol == api.ProtocolTCP {
1✔
657
                        // If the default is already TCP, no need to include protocol.
×
658
                        tcpPorts = append(tcpPorts, servicePort)
×
659
                } else {
1✔
660
                        servicePort.Protocol = protocol
1✔
661
                        udpPorts = append(udpPorts, servicePort)
1✔
662
                }
1✔
663
        }
664
        return tcpPorts, udpPorts
1✔
665
}
666

667
// ConfigServicePorts configure the container service ports.
668
func (k *Kubernetes) ConfigServicePorts(service kobject.ServiceConfig) []api.ServicePort {
26✔
669
        servicePorts := []api.ServicePort{}
26✔
670
        seenPorts := make(map[int]struct{}, len(service.Port))
26✔
671

26✔
672
        var servicePort api.ServicePort
26✔
673
        for _, port := range service.Port {
100✔
674
                if port.HostPort == 0 {
74✔
675
                        port.HostPort = port.ContainerPort
×
676
                }
×
677

678
                var targetPort intstr.IntOrString
74✔
679
                targetPort.IntVal = port.ContainerPort
74✔
680
                targetPort.StrVal = strconv.Itoa(int(port.ContainerPort))
74✔
681

74✔
682
                // decide the name based on whether we saw this port before
74✔
683
                name := strconv.Itoa(int(port.HostPort))
74✔
684
                if _, ok := seenPorts[int(port.HostPort)]; ok {
90✔
685
                        // https://github.com/kubernetes/kubernetes/issues/2995
16✔
686
                        if service.ServiceType == string(api.ServiceTypeLoadBalancer) {
16✔
687
                                log.Fatalf("Service %s of type LoadBalancer cannot use TCP and UDP for the same port", name)
×
688
                        }
×
689
                        name = fmt.Sprintf("%s-%s", name, strings.ToLower(port.Protocol))
16✔
690
                }
691

692
                servicePort = api.ServicePort{
74✔
693
                        Name:       name,
74✔
694
                        Port:       port.HostPort,
74✔
695
                        TargetPort: targetPort,
74✔
696
                }
74✔
697

74✔
698
                if service.ServiceType == string(api.ServiceTypeNodePort) && service.NodePortPort != 0 {
74✔
699
                        servicePort.NodePort = service.NodePortPort
×
700
                }
×
701

702
                // If the default is already TCP, no need to include protocol.
703
                if protocol := api.Protocol(port.Protocol); protocol != api.ProtocolTCP {
139✔
704
                        servicePort.Protocol = protocol
65✔
705
                }
65✔
706

707
                servicePorts = append(servicePorts, servicePort)
74✔
708
                seenPorts[int(port.HostPort)] = struct{}{}
74✔
709
        }
710
        return servicePorts
26✔
711
}
712

713
// ConfigCapabilities configure POSIX capabilities that can be added or removed to a container
714
func ConfigCapabilities(service kobject.ServiceConfig) *api.Capabilities {
44✔
715
        capsAdd := []api.Capability{}
44✔
716
        capsDrop := []api.Capability{}
44✔
717
        for _, capAdd := range service.CapAdd {
66✔
718
                capsAdd = append(capsAdd, api.Capability(capAdd))
22✔
719
        }
22✔
720
        for _, capDrop := range service.CapDrop {
65✔
721
                capsDrop = append(capsDrop, api.Capability(capDrop))
21✔
722
        }
21✔
723
        return &api.Capabilities{
44✔
724
                Add:  capsAdd,
44✔
725
                Drop: capsDrop,
44✔
726
        }
44✔
727
}
728

729
// ConfigTmpfs configure the tmpfs.
730
func (k *Kubernetes) ConfigTmpfs(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) {
17✔
731
        //initializing volumemounts and volumes
17✔
732
        volumeMounts := []api.VolumeMount{}
17✔
733
        volumes := []api.Volume{}
17✔
734

17✔
735
        for index, volume := range service.TmpFs {
34✔
736
                //naming volumes if multiple tmpfs are provided
17✔
737
                volumeName := fmt.Sprintf("%s-tmpfs%d", name, index)
17✔
738
                volume = strings.Split(volume, ":")[0]
17✔
739
                // create a new volume mount object and append to list
17✔
740
                volMount := api.VolumeMount{
17✔
741
                        Name:      volumeName,
17✔
742
                        MountPath: volume,
17✔
743
                }
17✔
744
                volumeMounts = append(volumeMounts, volMount)
17✔
745

17✔
746
                //create tmpfs specific empty volumes
17✔
747
                volSource := k.ConfigEmptyVolumeSource("tmpfs")
17✔
748

17✔
749
                // create a new volume object using the volsource and add to list
17✔
750
                vol := api.Volume{
17✔
751
                        Name:         volumeName,
17✔
752
                        VolumeSource: *volSource,
17✔
753
                }
17✔
754
                volumes = append(volumes, vol)
17✔
755
        }
17✔
756
        return volumeMounts, volumes
17✔
757
}
758

759
// ConfigSecretVolumes config volumes from secret.
760
// Link: https://docs.docker.com/compose/compose-file/#secrets
761
// In kubernetes' Secret resource, it has a data structure like a map[string]bytes, every key will act like the file name
762
// when mount to a container. This is the part that missing in compose. So we will create a single key secret from compose
763
// config and the key's name will be the secret's name, it's value is the file content.
764
// compose's secret can only be mounted at `/run/secrets`, so this will be hardcoded.
765
func (k *Kubernetes) ConfigSecretVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) {
42✔
766
        var volumeMounts []api.VolumeMount
42✔
767
        var volumes []api.Volume
42✔
768
        if len(service.Secrets) > 0 {
42✔
769
                for _, secretConfig := range service.Secrets {
×
770
                        if secretConfig.UID != "" {
×
771
                                log.Warnf("Ignore pid in secrets for service: %s", name)
×
772
                        }
×
773
                        if secretConfig.GID != "" {
×
774
                                log.Warnf("Ignore gid in secrets for service: %s", name)
×
775
                        }
×
776

777
                        var secretItemPath, secretMountPath, secretSubPath string
×
778
                        if k.Opt.SecretsAsFiles {
×
779
                                secretItemPath, secretMountPath, secretSubPath = k.getSecretPaths(secretConfig)
×
780
                        } else {
×
781
                                secretItemPath, secretMountPath, secretSubPath = k.getSecretPathsLegacy(secretConfig)
×
782
                        }
×
783

784
                        volSource := api.VolumeSource{
×
785
                                Secret: &api.SecretVolumeSource{
×
786
                                        SecretName: secretConfig.Source,
×
787
                                        Items: []api.KeyToPath{{
×
788
                                                Key:  secretConfig.Source,
×
789
                                                Path: secretItemPath,
×
790
                                        }},
×
791
                                },
×
792
                        }
×
793

×
794
                        if secretConfig.Mode != nil {
×
795
                                mode := cast.ToInt32(*secretConfig.Mode)
×
796
                                volSource.Secret.DefaultMode = &mode
×
797
                        }
×
798

799
                        vol := api.Volume{
×
800
                                Name:         secretConfig.Source,
×
801
                                VolumeSource: volSource,
×
802
                        }
×
803
                        volumes = append(volumes, vol)
×
804

×
805
                        volMount := api.VolumeMount{
×
806
                                Name:      vol.Name,
×
807
                                MountPath: secretMountPath,
×
808
                                SubPath:   secretSubPath,
×
809
                        }
×
810
                        volumeMounts = append(volumeMounts, volMount)
×
811
                }
812
        }
813
        return volumeMounts, volumes
42✔
814
}
815

816
func (k *Kubernetes) getSecretPaths(secretConfig types.ServiceSecretConfig) (secretItemPath, secretMountPath, secretSubPath string) {
×
817
        // Default secretConfig.Target to secretConfig.Source, just in case user was using short secret syntax or
×
818
        // otherwise did not define a specific target
×
819
        target := secretConfig.Target
×
820
        if target == "" {
×
821
                target = secretConfig.Source
×
822
        }
×
823

824
        // If target is an absolute path, set that as the MountPath
825
        if strings.HasPrefix(secretConfig.Target, "/") {
×
826
                secretMountPath = target
×
827
        } else {
×
828
                // If target is a relative path, prefix with "/run/secrets/" to replicate what docker-compose would do
×
829
                secretMountPath = "/run/secrets/" + target
×
830
        }
×
831

832
        // Set subPath to the target filename. this ensures that we end up with a file at our MountPath instead
833
        // of a directory with symlinks (see https://stackoverflow.com/a/68332231)
834
        splitPath := strings.Split(target, "/")
×
835
        secretFilename := splitPath[len(splitPath)-1]
×
836

×
837
        // `secretItemPath` and `secretSubPath` have to be the same as `secretFilename` to ensure we create a file with
×
838
        // that name at `secretMountPath`, instead of a directory containing a symlink to the actual file.
×
839
        secretItemPath = secretFilename
×
840
        secretSubPath = secretFilename
×
841

×
842
        return secretItemPath, secretMountPath, secretSubPath
×
843
}
844

845
func (k *Kubernetes) getSecretPathsLegacy(secretConfig types.ServiceSecretConfig) (secretItemPath, secretMountPath, secretSubPath string) {
×
846
        // The old way of setting secret paths. It resulted in files being placed in incorrect locations when compared to
×
847
        // docker-compose results, but some people might depend on this behavior so this is kept here for compatibility.
×
848
        // See https://github.com/kubernetes/kompose/issues/1280 for more details.
×
849

×
850
        var itemPath string // should be the filename
×
851
        var mountPath = ""  // should be the directory
×
852
        // if is used the short-syntax
×
853
        if secretConfig.Target == "" {
×
854
                // the secret path (mountPath) should be inside the default directory /run/secrets
×
855
                mountPath = "/run/secrets/" + secretConfig.Source
×
856
                // the itemPath should be the source itself
×
857
                itemPath = secretConfig.Source
×
858
        } else {
×
859
                // if is the long-syntax, i should get the last part of path and consider it the filename
×
860
                pathSplitted := strings.Split(secretConfig.Target, "/")
×
861
                lastPart := pathSplitted[len(pathSplitted)-1]
×
862

×
863
                // if the filename (lastPart) and the target is the same
×
864
                if lastPart == secretConfig.Target {
×
865
                        // the secret path should be the source (it need to be inside a directory and only the filename was given)
×
866
                        mountPath = secretConfig.Source
×
867
                } else {
×
868
                        // should then get the target without the filename (lastPart)
×
869
                        mountPath = mountPath + strings.TrimSuffix(secretConfig.Target, "/"+lastPart) // menos ultima parte
×
870
                }
×
871

872
                // if the target isn't absolute path
873
                if !strings.HasPrefix(secretConfig.Target, "/") {
×
874
                        // concat the default secret directory
×
875
                        mountPath = "/run/secrets/" + mountPath
×
876
                }
×
877

878
                itemPath = lastPart
×
879
        }
880

881
        secretSubPath = "" // We didn't set a SubPath in legacy behavior
×
882
        return itemPath, mountPath, ""
×
883
}
884

885
// ConfigVolumes configure the container volumes.
886
func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim, []*api.ConfigMap, error) {
42✔
887
        volumeMounts := []api.VolumeMount{}
42✔
888
        volumes := []api.Volume{}
42✔
889
        var PVCs []*api.PersistentVolumeClaim
42✔
890
        var cms []*api.ConfigMap
42✔
891
        var volumeName string
42✔
892
        var subpathName string
42✔
893

42✔
894
        // Set a var based on if the user wants to use empty volumes
42✔
895
        // as opposed to persistent volumes and volume claims
42✔
896
        useEmptyVolumes := k.Opt.EmptyVols
42✔
897
        useHostPath := k.Opt.Volumes == "hostPath"
42✔
898
        useConfigMap := k.Opt.Volumes == "configMap"
42✔
899
        if k.Opt.Volumes == "emptyDir" {
42✔
900
                useEmptyVolumes = true
×
901
        }
×
902

903
        if subpath, ok := service.Labels["kompose.volume.subpath"]; ok {
42✔
904
                subpathName = subpath
×
905
        }
×
906

907
        // Override volume type if specified in service labels.
908
        if vt, ok := service.Labels["kompose.volume.type"]; ok {
42✔
909
                if _, okk := ValidVolumeSet[vt]; !okk {
×
910
                        return nil, nil, nil, nil, fmt.Errorf("invalid volume type %s specified in label 'kompose.volume.type' in service %s", vt, service.Name)
×
911
                }
×
912
                useEmptyVolumes = vt == "emptyDir"
×
913
                useHostPath = vt == "hostPath"
×
914
                useConfigMap = vt == "configMap"
×
915
        }
916

917
        // config volumes from secret if present
918
        secretsVolumeMounts, secretsVolumes := k.ConfigSecretVolumes(name, service)
42✔
919
        volumeMounts = append(volumeMounts, secretsVolumeMounts...)
42✔
920
        volumes = append(volumes, secretsVolumes...)
42✔
921

42✔
922
        var count int
42✔
923
        //iterating over array of `Vols` struct as it contains all necessary information about volumes
42✔
924
        for _, volume := range service.Volumes {
63✔
925
                // check if ro/rw mode is defined, default rw
21✔
926
                readonly := len(volume.Mode) > 0 && volume.Mode == "ro"
21✔
927
                if volume.VolumeName == "" {
38✔
928
                        if useEmptyVolumes {
17✔
929
                                volumeName = strings.Replace(volume.PVCName, "claim", "empty", 1)
×
930
                        } else if useHostPath {
17✔
931
                                volumeName = strings.Replace(volume.PVCName, "claim", "hostpath", 1)
×
932
                        } else if useConfigMap {
17✔
933
                                volumeName = strings.Replace(volume.PVCName, "claim", "cm", 1)
×
934
                        } else {
17✔
935
                                volumeName = volume.PVCName
17✔
936
                        }
17✔
937
                        // to support service group bases on volume, we need use the new group name to replace the origin service name
938
                        // in volume name. For normal service, this should have no effect
939
                        volumeName = strings.Replace(volumeName, service.Name, name, 1)
17✔
940
                        count++
17✔
941
                } else {
4✔
942
                        volumeName = volume.VolumeName
4✔
943
                }
4✔
944
                volMount := api.VolumeMount{
21✔
945
                        Name:      volumeName,
21✔
946
                        ReadOnly:  readonly,
21✔
947
                        MountPath: volume.Container,
21✔
948
                }
21✔
949

21✔
950
                // Get a volume source based on the type of volume we are using
21✔
951
                // For PVC we will also create a PVC object and add to list
21✔
952
                var volsource *api.VolumeSource
21✔
953

21✔
954
                if useEmptyVolumes {
21✔
955
                        volsource = k.ConfigEmptyVolumeSource("volume")
×
956
                } else if useHostPath {
21✔
957
                        source, err := k.ConfigHostPathVolumeSource(volume.Host)
×
958
                        if err != nil {
×
959
                                return nil, nil, nil, nil, errors.Wrap(err, "k.ConfigHostPathVolumeSource failed")
×
960
                        }
×
961
                        volsource = source
×
962
                } else if useConfigMap {
21✔
963
                        log.Debugf("Use configmap volume")
×
964
                        cm, err := k.IntiConfigMapFromFileOrDir(name, volumeName, volume.Host, service)
×
965
                        if err != nil {
×
966
                                return nil, nil, nil, nil, err
×
967
                        }
×
968
                        cms = append(cms, cm)
×
969
                        volsource = k.ConfigConfigMapVolumeSource(volumeName, volume.Container, cm)
×
970

×
971
                        if useSubPathMount(cm) {
×
972
                                volMount.SubPath = volsource.ConfigMap.Items[0].Path
×
973
                        }
×
974
                } else {
21✔
975
                        volsource = k.ConfigPVCVolumeSource(volumeName, readonly)
21✔
976
                        if volume.VFrom == "" {
42✔
977
                                var storageClassName string
21✔
978
                                defaultSize := PVCRequestSize
21✔
979
                                if k.Opt.PVCRequestSize != "" {
21✔
980
                                        defaultSize = k.Opt.PVCRequestSize
×
981
                                }
×
982
                                if len(volume.PVCSize) > 0 {
21✔
983
                                        defaultSize = volume.PVCSize
×
984
                                } else {
21✔
985
                                        for key, value := range service.Labels {
27✔
986
                                                if key == "kompose.volume.size" {
6✔
987
                                                        defaultSize = value
×
988
                                                } else if key == "kompose.volume.storage-class-name" {
6✔
989
                                                        storageClassName = value
×
990
                                                }
×
991
                                        }
992
                                }
993

994
                                createdPVC, err := k.CreatePVC(volumeName, volume.Mode, defaultSize, volume.SelectorValue, storageClassName)
21✔
995

21✔
996
                                if err != nil {
21✔
997
                                        return nil, nil, nil, nil, errors.Wrap(err, "k.CreatePVC failed")
×
998
                                }
×
999

1000
                                PVCs = append(PVCs, createdPVC)
21✔
1001
                        }
1002
                }
1003
                if subpathName != "" {
21✔
1004
                        volMount.SubPath = subpathName
×
1005
                }
×
1006
                volumeMounts = append(volumeMounts, volMount)
21✔
1007

21✔
1008
                // create a new volume object using the volsource and add to list
21✔
1009
                vol := api.Volume{
21✔
1010
                        Name:         volumeName,
21✔
1011
                        VolumeSource: *volsource,
21✔
1012
                }
21✔
1013
                volumes = append(volumes, vol)
21✔
1014

21✔
1015
                if len(volume.Host) > 0 && (!useHostPath && !useConfigMap) {
21✔
1016
                        log.Warningf("Volume mount on the host %q isn't supported - ignoring path on the host", volume.Host)
×
1017
                }
×
1018
        }
1019

1020
        return volumeMounts, volumes, PVCs, cms, nil
42✔
1021
}
1022

1023
// ConfigEmptyVolumeSource is helper function to create an EmptyDir api.VolumeSource
1024
// either for Tmpfs or for emptyvolumes
1025
func (k *Kubernetes) ConfigEmptyVolumeSource(key string) *api.VolumeSource {
17✔
1026
        //if key is tmpfs
17✔
1027
        if key == "tmpfs" {
34✔
1028
                return &api.VolumeSource{
17✔
1029
                        EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory},
17✔
1030
                }
17✔
1031
        }
17✔
1032

1033
        //if key is volume
1034
        return &api.VolumeSource{
×
1035
                EmptyDir: &api.EmptyDirVolumeSource{},
×
1036
        }
×
1037
}
1038

1039
// ConfigConfigMapVolumeSource config a configmap to use as volume source
1040
func (k *Kubernetes) ConfigConfigMapVolumeSource(cmName string, targetPath string, cm *api.ConfigMap) *api.VolumeSource {
×
1041
        s := api.ConfigMapVolumeSource{}
×
1042
        s.Name = cmName
×
1043
        if useSubPathMount(cm) {
×
1044
                var keys []string
×
1045
                for k := range cm.Data {
×
1046
                        keys = append(keys, k)
×
1047
                }
×
1048
                for k := range cm.BinaryData {
×
1049
                        keys = append(keys, k)
×
1050
                }
×
1051
                key := keys[0]
×
1052
                _, p := path.Split(targetPath)
×
1053
                s.Items = []api.KeyToPath{
×
1054
                        {
×
1055
                                Key:  key,
×
1056
                                Path: p,
×
1057
                        },
×
1058
                }
×
1059
        }
1060
        return &api.VolumeSource{
×
1061
                ConfigMap: &s,
×
1062
        }
×
1063
}
1064

1065
// ConfigHostPathVolumeSource is a helper function to create a HostPath api.VolumeSource
1066
func (k *Kubernetes) ConfigHostPathVolumeSource(path string) (*api.VolumeSource, error) {
×
1067
        dir, err := transformer.GetComposeFileDir(k.Opt.InputFiles)
×
1068
        if err != nil {
×
1069
                return nil, err
×
1070
        }
×
1071
        absPath := path
×
1072
        if !filepath.IsAbs(path) {
×
1073
                absPath = filepath.Join(dir, path)
×
1074
        }
×
1075

1076
        return &api.VolumeSource{
×
1077
                HostPath: &api.HostPathVolumeSource{Path: absPath},
×
1078
        }, nil
×
1079
}
1080

1081
// ConfigPVCVolumeSource is helper function to create an api.VolumeSource with a PVC
1082
func (k *Kubernetes) ConfigPVCVolumeSource(name string, readonly bool) *api.VolumeSource {
21✔
1083
        return &api.VolumeSource{
21✔
1084
                PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{
21✔
1085
                        ClaimName: name,
21✔
1086
                        ReadOnly:  readonly,
21✔
1087
                },
21✔
1088
        }
21✔
1089
}
21✔
1090

1091
// ConfigEnvs configures the environment variables.
1092
func ConfigEnvs(service kobject.ServiceConfig, opt kobject.ConvertOptions) ([]api.EnvVar, error) {
42✔
1093
        envs := transformer.EnvSort{}
42✔
1094

42✔
1095
        keysFromEnvFile := make(map[string]bool)
42✔
1096

42✔
1097
        // If there is an env_file, use ConfigMaps and ignore the environment variables
42✔
1098
        // already specified
42✔
1099

42✔
1100
        if len(service.EnvFile) > 0 {
42✔
1101
                // Load each env_file
×
1102
                for _, file := range service.EnvFile {
×
1103
                        envName := FormatEnvName(file)
×
1104

×
1105
                        // Load environment variables from file
×
1106
                        envLoad, err := GetEnvsFromFile(file)
×
1107
                        if err != nil {
×
1108
                                return envs, errors.Wrap(err, "Unable to read env_file")
×
1109
                        }
×
1110

1111
                        // Add configMapKeyRef to each environment variable
1112
                        for k := range envLoad {
×
1113
                                envs = append(envs, api.EnvVar{
×
1114
                                        Name: k,
×
1115
                                        ValueFrom: &api.EnvVarSource{
×
1116
                                                ConfigMapKeyRef: &api.ConfigMapKeySelector{
×
1117
                                                        LocalObjectReference: api.LocalObjectReference{
×
1118
                                                                Name: envName,
×
1119
                                                        },
×
1120
                                                        Key: k,
×
1121
                                                }},
×
1122
                                })
×
1123
                                keysFromEnvFile[k] = true
×
1124
                        }
×
1125
                }
1126
        }
1127

1128
        // Load up the environment variables
1129
        for _, v := range service.Environment {
66✔
1130
                if !keysFromEnvFile[v.Name] {
48✔
1131
                        envs = append(envs, api.EnvVar{
24✔
1132
                                Name:  v.Name,
24✔
1133
                                Value: v.Value,
24✔
1134
                        })
24✔
1135
                }
24✔
1136
        }
1137

1138
        // Stable sorts data while keeping the original order of equal elements
1139
        // we need this because envs are not populated in any random order
1140
        // this sorting ensures they are populated in a particular order
1141
        sort.Stable(envs)
42✔
1142
        return envs, nil
42✔
1143
}
1144

1145
// ConfigAffinity configures the Affinity.
1146
func ConfigAffinity(service kobject.ServiceConfig) *api.Affinity {
36✔
1147
        var affinity *api.Affinity
36✔
1148
        // Config constraints
36✔
1149
        // Convert constraints to requiredDuringSchedulingIgnoredDuringExecution
36✔
1150
        positiveConstraints := configConstrains(service.Placement.PositiveConstraints, api.NodeSelectorOpIn)
36✔
1151
        negativeConstraints := configConstrains(service.Placement.NegativeConstraints, api.NodeSelectorOpNotIn)
36✔
1152
        if len(positiveConstraints) != 0 || len(negativeConstraints) != 0 {
37✔
1153
                affinity = &api.Affinity{
1✔
1154
                        NodeAffinity: &api.NodeAffinity{
1✔
1155
                                RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
1✔
1156
                                        NodeSelectorTerms: []api.NodeSelectorTerm{
1✔
1157
                                                {
1✔
1158
                                                        MatchExpressions: append(positiveConstraints, negativeConstraints...),
1✔
1159
                                                },
1✔
1160
                                        },
1✔
1161
                                },
1✔
1162
                        },
1✔
1163
                }
1✔
1164
        }
1✔
1165
        return affinity
36✔
1166
}
1167

1168
// ConfigTopologySpreadConstraints configures the TopologySpreadConstraints.
1169
func ConfigTopologySpreadConstraints(service kobject.ServiceConfig) []api.TopologySpreadConstraint {
35✔
1170
        preferencesLen := len(service.Placement.Preferences)
35✔
1171
        constraints := make([]api.TopologySpreadConstraint, 0, preferencesLen)
35✔
1172

35✔
1173
        // Placement preferences are ignored for global services
35✔
1174
        if service.DeployMode == "global" {
35✔
1175
                log.Warnf("Ignore placement preferences for global service %s", service.Name)
×
1176
                return constraints
×
1177
        }
×
1178

1179
        for i, p := range service.Placement.Preferences {
37✔
1180
                constraints = append(constraints, api.TopologySpreadConstraint{
2✔
1181
                        // According to the order of preferences, the MaxSkew decreases in order
2✔
1182
                        // The minimum value is 1
2✔
1183
                        MaxSkew:           int32(preferencesLen - i),
2✔
1184
                        TopologyKey:       p,
2✔
1185
                        WhenUnsatisfiable: api.ScheduleAnyway,
2✔
1186
                        LabelSelector: &metav1.LabelSelector{
2✔
1187
                                MatchLabels: transformer.ConfigLabels(service.Name),
2✔
1188
                        },
2✔
1189
                })
2✔
1190
        }
2✔
1191

1192
        return constraints
35✔
1193
}
1194

1195
func configConstrains(constrains map[string]string, operator api.NodeSelectorOperator) []api.NodeSelectorRequirement {
72✔
1196
        constraintsLen := len(constrains)
72✔
1197
        rs := make([]api.NodeSelectorRequirement, 0, constraintsLen)
72✔
1198
        if constraintsLen == 0 {
142✔
1199
                return rs
70✔
1200
        }
70✔
1201
        for k, v := range constrains {
4✔
1202
                r := api.NodeSelectorRequirement{
2✔
1203
                        Key:      k,
2✔
1204
                        Operator: operator,
2✔
1205
                        Values:   []string{v},
2✔
1206
                }
2✔
1207
                rs = append(rs, r)
2✔
1208
        }
2✔
1209
        return rs
2✔
1210
}
1211

1212
// CreateWorkloadAndConfigMapObjects generates a Kubernetes artifact for each input type service
1213
func (k *Kubernetes) CreateWorkloadAndConfigMapObjects(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) []runtime.Object {
40✔
1214
        var objects []runtime.Object
40✔
1215
        var replica int
40✔
1216

40✔
1217
        if opt.IsReplicaSetFlag || service.Replicas == 0 {
67✔
1218
                replica = opt.Replicas
27✔
1219
        } else {
40✔
1220
                replica = service.Replicas
13✔
1221
        }
13✔
1222

1223
        // Check to see if Docker Compose v3 Deploy.Mode has been set to "global"
1224
        if service.DeployMode == "global" {
40✔
1225
                //default use daemonset
×
1226
                if opt.Controller == "" {
×
1227
                        opt.CreateD = false
×
1228
                        opt.CreateDS = true
×
1229
                } else if opt.Controller != "daemonset" {
×
1230
                        log.Warnf("Global deploy mode service is best converted to daemonset, now it convert to %s", opt.Controller)
×
1231
                }
×
1232
        }
1233

1234
        //Resolve labels first
1235
        if val, ok := service.Labels[compose.LabelControllerType]; ok {
40✔
1236
                opt.CreateD = false
×
1237
                opt.CreateDS = false
×
1238
                opt.CreateRC = false
×
1239
                if opt.Controller != "" {
×
1240
                        log.Warnf("Use label %s type %s for service %s, ignore %s flags", compose.LabelControllerType, val, name, opt.Controller)
×
1241
                }
×
1242
                opt.Controller = val
×
1243
        }
1244

1245
        if len(service.Configs) > 0 {
56✔
1246
                objects = k.createConfigMapFromComposeConfig(name, service, objects)
16✔
1247
        }
16✔
1248

1249
        if opt.CreateD || opt.Controller == DeploymentController {
67✔
1250
                objects = append(objects, k.InitD(name, service, replica))
27✔
1251
        }
27✔
1252

1253
        if opt.CreateDS || opt.Controller == DaemonSetController {
43✔
1254
                objects = append(objects, k.InitDS(name, service))
3✔
1255
        }
3✔
1256

1257
        if opt.Controller == StatefulStateController {
42✔
1258
                objects = append(objects, k.InitSS(name, service, replica))
2✔
1259
        }
2✔
1260

1261
        if len(service.EnvFile) > 0 {
40✔
1262
                for _, envFile := range service.EnvFile {
×
1263
                        configMap := k.InitConfigMapForEnv(name, opt, envFile)
×
1264
                        objects = append(objects, configMap)
×
1265
                }
×
1266
        }
1267

1268
        return objects
40✔
1269
}
1270

1271
func (k *Kubernetes) createConfigMapFromComposeConfig(name string, service kobject.ServiceConfig, objects []runtime.Object) []runtime.Object {
16✔
1272
        for _, config := range service.Configs {
32✔
1273
                currentConfigName := config.Source
16✔
1274
                currentConfigObj := service.ConfigsMetaData[currentConfigName]
16✔
1275
                if currentConfigObj.External.External {
16✔
1276
                        continue
×
1277
                }
1278
                currentFileName := currentConfigObj.File
16✔
1279
                configMap := k.InitConfigMapFromFile(name, service, currentFileName)
16✔
1280
                objects = append(objects, configMap)
16✔
1281
        }
1282
        return objects
16✔
1283
}
1284

1285
// InitPod initializes Kubernetes Pod object
1286
func (k *Kubernetes) InitPod(name string, service kobject.ServiceConfig) *api.Pod {
2✔
1287
        pod := api.Pod{
2✔
1288
                TypeMeta: metav1.TypeMeta{
2✔
1289
                        Kind:       "Pod",
2✔
1290
                        APIVersion: "v1",
2✔
1291
                },
2✔
1292
                ObjectMeta: metav1.ObjectMeta{
2✔
1293
                        Name:        name,
2✔
1294
                        Labels:      transformer.ConfigLabels(name),
2✔
1295
                        Annotations: transformer.ConfigAnnotations(service),
2✔
1296
                },
2✔
1297
                Spec: k.InitPodSpec(name, service.Image, service.ImagePullSecret),
2✔
1298
        }
2✔
1299
        return &pod
2✔
1300
}
2✔
1301

1302
// CreateNetworkPolicy initializes Network policy
1303
func (k *Kubernetes) CreateNetworkPolicy(networkName string) (*networkingv1.NetworkPolicy, error) {
4✔
1304
        str := "true"
4✔
1305
        np := &networkingv1.NetworkPolicy{
4✔
1306
                TypeMeta: metav1.TypeMeta{
4✔
1307
                        Kind:       "NetworkPolicy",
4✔
1308
                        APIVersion: "networking.k8s.io/v1",
4✔
1309
                },
4✔
1310
                ObjectMeta: metav1.ObjectMeta{
4✔
1311
                        Name: networkName,
4✔
1312
                        //Labels: transformer.ConfigLabels(name)(name),
4✔
1313
                },
4✔
1314
                Spec: networkingv1.NetworkPolicySpec{
4✔
1315
                        PodSelector: metav1.LabelSelector{
4✔
1316
                                MatchLabels: map[string]string{"io.kompose.network/" + networkName: str},
4✔
1317
                        },
4✔
1318
                        Ingress: []networkingv1.NetworkPolicyIngressRule{{
4✔
1319
                                From: []networkingv1.NetworkPolicyPeer{{
4✔
1320
                                        PodSelector: &metav1.LabelSelector{
4✔
1321
                                                MatchLabels: map[string]string{"io.kompose.network/" + networkName: str},
4✔
1322
                                        },
4✔
1323
                                }},
4✔
1324
                        }},
4✔
1325
                },
4✔
1326
        }
4✔
1327

4✔
1328
        return np, nil
4✔
1329
}
4✔
1330

1331
func buildServiceImage(opt kobject.ConvertOptions, service kobject.ServiceConfig, name string) error {
42✔
1332
        // Must build the images before conversion (got to add service.Image in case 'image' key isn't provided
42✔
1333
        // Check that --build is set to true
42✔
1334
        // Check to see if there is an InputFile (required!) before we build the container
42✔
1335
        // Check that there's actually a Build key
42✔
1336
        // Lastly, we must have an Image name to continue
42✔
1337

42✔
1338
        // If the user provided a custom build it will override the docker one.
42✔
1339
        if opt.BuildCommand != "" && opt.PushCommand != "" {
42✔
1340
                p := shellwords.NewParser()
×
1341
                p.ParseEnv = true
×
1342

×
1343
                buildArgs, _ := p.Parse(opt.BuildCommand)
×
1344
                buildCommand := exec.Command(buildArgs[0], buildArgs[1:]...)
×
1345
                err := buildCommand.Run()
×
1346
                if err != nil {
×
1347
                        return errors.Wrap(err, "error while trying to build a custom container image")
×
1348
                }
×
1349

1350
                pushArgs, _ := p.Parse(opt.PushCommand)
×
1351
                pushCommand := exec.Command(pushArgs[0], pushArgs[1:]...)
×
1352
                err = pushCommand.Run()
×
1353
                if err != nil {
×
1354
                        return errors.Wrap(err, "error while trying to push a custom container image")
×
1355
                }
×
1356
                return nil
×
1357
        }
1358
        if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" {
42✔
1359
                // If there's no "image" key, use the name of the container that's built
×
1360
                if service.Image == "" {
×
1361
                        service.Image = name
×
1362
                }
×
1363

1364
                if service.Image == "" {
×
1365
                        return fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name)
×
1366
                }
×
1367

1368
                log.Infof("Build key detected. Attempting to build image '%s'", service.Image)
×
1369

×
1370
                // Build the image!
×
1371
                err := transformer.BuildDockerImage(service, name)
×
1372
                if err != nil {
×
1373
                        return errors.Wrapf(err, "Unable to build Docker image for service %v", name)
×
1374
                }
×
1375

1376
                // Push the built image to the repo!
1377
                err = transformer.PushDockerImageWithOpt(service, name, opt)
×
1378
                if err != nil {
×
1379
                        return errors.Wrapf(err, "Unable to push Docker image for service %v", name)
×
1380
                }
×
1381
        }
1382
        return nil
42✔
1383
}
1384

1385
func (k *Kubernetes) configKubeServiceAndIngressForService(service kobject.ServiceConfig, name string, objects *[]runtime.Object) {
42✔
1386
        if k.PortsExist(service) {
68✔
1387
                if service.ServiceType == "LoadBalancer" {
27✔
1388
                        svcs := k.CreateLBService(name, service)
1✔
1389
                        for _, svc := range svcs {
2✔
1390
                                svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyType(service.ServiceExternalTrafficPolicy)
1✔
1391
                                *objects = append(*objects, svc)
1✔
1392
                        }
1✔
1393
                        if len(svcs) > 1 {
1✔
1394
                                log.Warningf("Create multiple service to avoid using mixed protocol in the same service when it's loadbalancer type")
×
1395
                        }
×
1396
                } else {
25✔
1397
                        svc := k.CreateService(name, service)
25✔
1398
                        *objects = append(*objects, svc)
25✔
1399
                        if service.ExposeService != "" {
27✔
1400
                                *objects = append(*objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
2✔
1401
                        }
2✔
1402
                        if service.ServiceExternalTrafficPolicy != "" && svc.Spec.Type != api.ServiceTypeNodePort {
25✔
1403
                                log.Warningf("External Traffic Policy is ignored for the service %v of type %v", name, service.ServiceType)
×
1404
                        }
×
1405
                }
1406
        } else {
16✔
1407
                if service.ServiceType == "Headless" {
20✔
1408
                        svc := k.CreateHeadlessService(name, service)
4✔
1409
                        *objects = append(*objects, svc)
4✔
1410
                        if service.ServiceExternalTrafficPolicy != "" {
4✔
1411
                                log.Warningf("External Traffic Policy is ignored for the service %v of type Headless", name)
×
1412
                        }
×
1413
                } else {
12✔
1414
                        log.Warnf("Service %q won't be created because 'ports' is not specified", service.Name)
12✔
1415
                }
12✔
1416
        }
1417
}
1418

1419
func (k *Kubernetes) configNetworkPolicyForService(service kobject.ServiceConfig, name string, objects *[]runtime.Object) error {
2✔
1420
        if len(service.Network) > 0 {
4✔
1421
                for _, net := range service.Network {
6✔
1422
                        log.Infof("Network %s is detected at Source, shall be converted to equivalent NetworkPolicy at Destination", net)
4✔
1423
                        np, err := k.CreateNetworkPolicy(net)
4✔
1424

4✔
1425
                        if err != nil {
4✔
1426
                                return errors.Wrapf(err, "Unable to create Network Policy for network %v for service %v", net, name)
×
1427
                        }
×
1428
                        *objects = append(*objects, np)
4✔
1429
                }
1430
        }
1431
        return nil
2✔
1432
}
1433

1434
// Transform maps komposeObject to k8s objects
1435
// returns object that are already sorted in the way that Services are first
1436
func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.ConvertOptions) ([]runtime.Object, error) {
39✔
1437
        // this will hold all the converted data
39✔
1438
        var allobjects []runtime.Object
39✔
1439

39✔
1440
        if komposeObject.Secrets != nil {
39✔
1441
                secrets, err := k.CreateSecrets(komposeObject)
×
1442
                if err != nil {
×
1443
                        return nil, errors.Wrapf(err, "Unable to create Secret resource")
×
1444
                }
×
1445
                for _, item := range secrets {
×
1446
                        allobjects = append(allobjects, item)
×
1447
                }
×
1448
        }
1449

1450
        if komposeObject.Namespace != "" {
40✔
1451
                ns := transformer.CreateNamespace(komposeObject.Namespace)
1✔
1452
                allobjects = append(allobjects, ns)
1✔
1453
        }
1✔
1454

1455
        if opt.ServiceGroupMode != "" {
47✔
1456
                log.Debugf("Service group mode is: %s", opt.ServiceGroupMode)
8✔
1457
                komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(&komposeObject, opt)
8✔
1458
                sortedGroupMappingKeys := SortedKeys(komposeObjectToServiceConfigGroupMapping)
8✔
1459
                for _, group := range sortedGroupMappingKeys {
11✔
1460
                        groupMapping := komposeObjectToServiceConfigGroupMapping[group]
3✔
1461
                        var objects []runtime.Object
3✔
1462
                        podSpec := PodSpec{}
3✔
1463

3✔
1464
                        var groupName string
3✔
1465
                        // if using volume group, the name here will be a volume config string. reset to the first service name
3✔
1466
                        if opt.ServiceGroupMode == "volume" {
3✔
1467
                                if opt.ServiceGroupName != "" {
×
1468
                                        groupName = opt.ServiceGroupName
×
1469
                                } else {
×
1470
                                        var names []string
×
1471
                                        for _, svc := range groupMapping {
×
1472
                                                names = append(names, svc.Name)
×
1473
                                        }
×
1474
                                        groupName = strings.Join(names, "-")
×
1475
                                }
1476
                        } else {
3✔
1477
                                groupName = group
3✔
1478
                        }
3✔
1479

1480
                        // added a container
1481
                        // ports conflict check between services
1482
                        portsUses := map[string]bool{}
3✔
1483

3✔
1484
                        for _, service := range groupMapping {
9✔
1485
                                // first do ports check
6✔
1486
                                ports := ConfigPorts(service)
6✔
1487
                                for _, port := range ports {
6✔
1488
                                        key := string(port.ContainerPort) + string(port.Protocol)
×
1489
                                        if portsUses[key] {
×
1490
                                                return nil, fmt.Errorf("detect ports conflict when group services, service: %s, port: %d", service.Name, port.ContainerPort)
×
1491
                                        }
×
1492
                                        portsUses[key] = true
×
1493
                                }
1494

1495
                                log.Infof("Group Service %s to [%s]", service.Name, groupName)
6✔
1496
                                service.WithKomposeAnnotation = opt.WithKomposeAnnotation
6✔
1497
                                podSpec.Append(AddContainer(service, opt))
6✔
1498

6✔
1499
                                if err := buildServiceImage(opt, service, service.Name); err != nil {
6✔
1500
                                        return nil, err
×
1501
                                }
×
1502
                                // override..
1503
                                objects = append(objects, k.CreateWorkloadAndConfigMapObjects(groupName, service, opt)...)
6✔
1504
                                k.configKubeServiceAndIngressForService(service, groupName, &objects)
6✔
1505

6✔
1506
                                // Configure the container volumes.
6✔
1507
                                volumesMount, volumes, pvc, cms, err := k.ConfigVolumes(groupName, service)
6✔
1508
                                if err != nil {
6✔
1509
                                        return nil, errors.Wrap(err, "k.ConfigVolumes failed")
×
1510
                                }
×
1511
                                // Configure Tmpfs
1512
                                if len(service.TmpFs) > 0 {
6✔
1513
                                        TmpVolumesMount, TmpVolumes := k.ConfigTmpfs(groupName, service)
×
1514
                                        volumes = append(volumes, TmpVolumes...)
×
1515
                                        volumesMount = append(volumesMount, TmpVolumesMount...)
×
1516
                                }
×
1517
                                podSpec.Append(
6✔
1518
                                        SetVolumeMounts(volumesMount),
6✔
1519
                                        SetVolumes(volumes),
6✔
1520
                                )
6✔
1521

6✔
1522
                                // Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
6✔
1523
                                // because the type of objects and pvc is different, but when doing append
6✔
1524
                                // one element at a time it gets converted to runtime.Object for objects slice
6✔
1525
                                for _, p := range pvc {
10✔
1526
                                        objects = append(objects, p)
4✔
1527
                                }
4✔
1528

1529
                                for _, c := range cms {
6✔
1530
                                        objects = append(objects, c)
×
1531
                                }
×
1532

1533
                                podSpec.Append(
6✔
1534
                                        SetPorts(service),
6✔
1535
                                        ImagePullPolicy(groupName, service),
6✔
1536
                                        RestartPolicy(groupName, service),
6✔
1537
                                        SecurityContext(groupName, service),
6✔
1538
                                        HostName(service),
6✔
1539
                                        DomainName(service),
6✔
1540
                                        ResourcesLimits(service),
6✔
1541
                                        ResourcesRequests(service),
6✔
1542
                                        TerminationGracePeriodSeconds(groupName, service),
6✔
1543
                                        TopologySpreadConstraints(service),
6✔
1544
                                )
6✔
1545

6✔
1546
                                if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok {
8✔
1547
                                        podSpec.Append(ServiceAccountName(serviceAccountName))
2✔
1548
                                }
2✔
1549

1550
                                err = k.UpdateKubernetesObjectsMultipleContainers(groupName, service, &objects, podSpec)
6✔
1551
                                if err != nil {
6✔
1552
                                        return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
×
1553
                                }
×
1554

1555
                                if opt.GenerateNetworkPolicies {
6✔
1556
                                        if err = k.configNetworkPolicyForService(service, service.Name, &objects); err != nil {
×
1557
                                                return nil, err
×
1558
                                        }
×
1559
                                }
1560
                        }
1561

1562
                        allobjects = append(allobjects, objects...)
3✔
1563
                }
1564
        }
1565
        sortedKeys := SortedKeys(komposeObject.ServiceConfigs)
39✔
1566
        for _, name := range sortedKeys {
81✔
1567
                service := komposeObject.ServiceConfigs[name]
42✔
1568

42✔
1569
                // if service belongs to a group, we already processed it
42✔
1570
                if service.InGroup {
48✔
1571
                        continue
6✔
1572
                }
1573

1574
                var objects []runtime.Object
36✔
1575

36✔
1576
                service.WithKomposeAnnotation = opt.WithKomposeAnnotation
36✔
1577

36✔
1578
                if err := buildServiceImage(opt, service, name); err != nil {
36✔
1579
                        return nil, err
×
1580
                }
×
1581

1582
                // Generate pod only and nothing more
1583
                if (service.Restart == "no" || service.Restart == "on-failure") && !opt.IsPodController() {
38✔
1584
                        log.Infof("Create kubernetes pod instead of pod controller due to restart policy: %s", service.Restart)
2✔
1585
                        pod := k.InitPod(name, service)
2✔
1586
                        objects = append(objects, pod)
2✔
1587
                } else {
36✔
1588
                        objects = k.CreateWorkloadAndConfigMapObjects(name, service, opt)
34✔
1589
                }
34✔
1590
                if opt.Controller == StatefulStateController {
38✔
1591
                        service.ServiceType = "Headless"
2✔
1592
                }
2✔
1593
                k.configKubeServiceAndIngressForService(service, name, &objects)
36✔
1594
                err := k.UpdateKubernetesObjects(name, service, opt, &objects)
36✔
1595
                if err != nil {
36✔
1596
                        return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
×
1597
                }
×
1598
                if opt.GenerateNetworkPolicies {
38✔
1599
                        if err := k.configNetworkPolicyForService(service, name, &objects); err != nil {
2✔
1600
                                return nil, err
×
1601
                        }
×
1602
                }
1603
                allobjects = append(allobjects, objects...)
36✔
1604
        }
1605

1606
        // sort all object so Services are first
1607
        k.SortServicesFirst(&allobjects)
39✔
1608
        k.RemoveDupObjects(&allobjects)
39✔
1609

39✔
1610
        // Only append namespaces if --namespace has been passed in
39✔
1611
        if komposeObject.Namespace != "" {
40✔
1612
                transformer.AssignNamespaceToObjects(&allobjects, komposeObject.Namespace)
1✔
1613
        }
1✔
1614
        // k.FixWorkloadVersion(&allobjects)
1615
        return allobjects, nil
39✔
1616
}
1617

1618
// UpdateController updates the given object with the given pod template update function and ObjectMeta update function
1619
func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*api.PodTemplateSpec) error, updateMeta func(meta *metav1.ObjectMeta)) (err error) {
106✔
1620
        switch t := obj.(type) {
106✔
1621
        case *appsv1.Deployment:
30✔
1622
                err = updateTemplate(&t.Spec.Template)
30✔
1623
                if err != nil {
30✔
1624
                        return errors.Wrap(err, "updateTemplate failed")
×
1625
                }
×
1626
                updateMeta(&t.ObjectMeta)
30✔
1627
        case *appsv1.DaemonSet:
3✔
1628
                err = updateTemplate(&t.Spec.Template)
3✔
1629
                if err != nil {
3✔
1630
                        return errors.Wrap(err, "updateTemplate failed")
×
1631
                }
×
1632
                updateMeta(&t.ObjectMeta)
3✔
1633
        case *appsv1.StatefulSet:
2✔
1634
                err = updateTemplate(&t.Spec.Template)
2✔
1635
                if err != nil {
2✔
1636
                        return errors.Wrap(err, "updateTemplate failed")
×
1637
                }
×
1638
                updateMeta(&t.ObjectMeta)
2✔
1639
        case *deployapi.DeploymentConfig:
×
1640
                err = updateTemplate(t.Spec.Template)
×
1641
                if err != nil {
×
1642
                        return errors.Wrap(err, "updateTemplate failed")
×
1643
                }
×
1644
                updateMeta(&t.ObjectMeta)
×
1645
        case *api.Pod:
2✔
1646
                p := api.PodTemplateSpec{
2✔
1647
                        ObjectMeta: t.ObjectMeta,
2✔
1648
                        Spec:       t.Spec,
2✔
1649
                }
2✔
1650
                err = updateTemplate(&p)
2✔
1651
                if err != nil {
2✔
1652
                        return errors.Wrap(err, "updateTemplate failed")
×
1653
                }
×
1654
                t.Spec = p.Spec
2✔
1655
                t.ObjectMeta = p.ObjectMeta
2✔
1656
        case *buildapi.BuildConfig:
×
1657
                updateMeta(&t.ObjectMeta)
×
1658
        }
1659
        return nil
106✔
1660
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc