forked from LaconicNetwork/kompose
Merge branch 'main' into feature-1631-add-hpa
This commit is contained in:
commit
405ab61479
@ -211,6 +211,9 @@ The currently supported options are:
|
|||||||
| kompose.cronjob.schedule | kubernetes cronjob schedule (for example: '1 * * * *') |
|
| kompose.cronjob.schedule | kubernetes cronjob schedule (for example: '1 * * * *') |
|
||||||
| kompose.cronjob.concurrency_policy | 'Forbid' / 'Allow' / 'Never' / '' |
|
| kompose.cronjob.concurrency_policy | 'Forbid' / 'Allow' / 'Never' / '' |
|
||||||
| kompose.cronjob.backoff_limit | kubernetes cronjob backoff limit (for example: '6') |
|
| kompose.cronjob.backoff_limit | kubernetes cronjob backoff limit (for example: '6') |
|
||||||
|
| kompose.init.containers.name | kubernetes init container name |
|
||||||
|
| kompose.init.containers.image | kubernetes init container image |
|
||||||
|
| kompose.init.containers.command | kubernetes init container commands |
|
||||||
| kompose.hpa.replicas.min | defines Horizontal Pod Autoscaler minimum number of pod replicas |
|
| kompose.hpa.replicas.min | defines Horizontal Pod Autoscaler minimum number of pod replicas |
|
||||||
| kompose.hpa.replicas.max | defines Horizontal Pod Autoscaler maximum number of pod replicas |
|
| kompose.hpa.replicas.max | defines Horizontal Pod Autoscaler maximum number of pod replicas |
|
||||||
| kompose.hpa.cpu | defines Horizontal Pod Autoscaler cpu utilization trigger |
|
| kompose.hpa.cpu | defines Horizontal Pod Autoscaler cpu utilization trigger |
|
||||||
@ -472,6 +475,48 @@ services:
|
|||||||
kompose.volume.sub-path: pg-data
|
kompose.volume.sub-path: pg-data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `kompose.init.containers.name` is used to specify the name of the Init Containers for a Pod [Init Container Name](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
example-service:
|
||||||
|
image: example-image
|
||||||
|
labels:
|
||||||
|
kompose.init.containers.name: "initcontainername"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `kompose.init.containers.image` defines image to use for the Init Containers [Init Container Image](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
example-service:
|
||||||
|
image: example-image
|
||||||
|
labels:
|
||||||
|
kompose.init.containers.image: perl
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
- `kompose.init.containers.command` defines the command that the Init Containers will run after they are started [Init Container Command](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
example-service:
|
||||||
|
image: example-image
|
||||||
|
labels:
|
||||||
|
kompose.init.containers.command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
|
||||||
|
kompose.init.containers.image: perl
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
- `kompose.hpa.replicas.min` defines the floor for the number of replicas that the HPA can scale down to during a scaling event. Default value is set to 1. This means that, regardless of the load on the system, the HPA will always maintain at least one replica. More info: [HPA Min Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
- `kompose.hpa.replicas.min` defines the floor for the number of replicas that the HPA can scale down to during a scaling event. Default value is set to 1. This means that, regardless of the load on the system, the HPA will always maintain at least one replica. More info: [HPA Min Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|||||||
@ -87,6 +87,12 @@ const (
|
|||||||
LabelCronJobConcurrencyPolicy = "kompose.cronjob.concurrency_policy"
|
LabelCronJobConcurrencyPolicy = "kompose.cronjob.concurrency_policy"
|
||||||
// LabelCronJobBackoffLimit defines the job backoff limit
|
// LabelCronJobBackoffLimit defines the job backoff limit
|
||||||
LabelCronJobBackoffLimit = "kompose.cronjob.backoff_limit"
|
LabelCronJobBackoffLimit = "kompose.cronjob.backoff_limit"
|
||||||
|
// LabelInitContainerName defines name resource
|
||||||
|
LabelInitContainerName = "kompose.init.containers.name"
|
||||||
|
// LabelInitContainerImage defines image to pull
|
||||||
|
LabelInitContainerImage = "kompose.init.containers.image"
|
||||||
|
// LabelInitContainerCommand defines commands
|
||||||
|
LabelInitContainerCommand = "kompose.init.containers.command"
|
||||||
// LabelHpaMinReplicas defines min pod replicas
|
// LabelHpaMinReplicas defines min pod replicas
|
||||||
LabelHpaMinReplicas = "kompose.hpa.replicas.min"
|
LabelHpaMinReplicas = "kompose.hpa.replicas.min"
|
||||||
// LabelHpaMaxReplicas defines max pod replicas
|
// LabelHpaMaxReplicas defines max pod replicas
|
||||||
|
|||||||
@ -678,7 +678,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
|||||||
if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok {
|
if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok {
|
||||||
template.Spec.ServiceAccountName = serviceAccountName
|
template.Spec.ServiceAccountName = serviceAccountName
|
||||||
}
|
}
|
||||||
|
fillInitContainers(template, service)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,6 +1010,51 @@ func reformatSecretConfigUnderscoreWithDash(secretConfig types.ServiceSecretConf
|
|||||||
return newSecretConfig
|
return newSecretConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fillInitContainers looks for an initContainer resources and its passed as labels
|
||||||
|
// if there is no image, it does not fill the initContainer
|
||||||
|
// https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
|
||||||
|
func fillInitContainers(template *api.PodTemplateSpec, service kobject.ServiceConfig) {
|
||||||
|
resourceImage, exist := service.Labels[compose.LabelInitContainerImage]
|
||||||
|
if !exist || resourceImage == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resourceName, exist := service.Labels[compose.LabelInitContainerName]
|
||||||
|
if !exist || resourceName == "" {
|
||||||
|
resourceName = "init-service"
|
||||||
|
}
|
||||||
|
|
||||||
|
template.Spec.InitContainers = append(template.Spec.InitContainers, api.Container{
|
||||||
|
Name: resourceName,
|
||||||
|
Command: parseContainerCommandsFromStr(service.Labels[compose.LabelInitContainerCommand]),
|
||||||
|
Image: resourceImage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseContainerCommandsFromStr parses a string containing comma-separated commands
|
||||||
|
// returns a slice of strings or a single command
|
||||||
|
// example:
|
||||||
|
// [ "bundle", "exec", "thin", "-p", "3000" ]
|
||||||
|
//
|
||||||
|
// example:
|
||||||
|
// [ "bundle exec thin -p 3000" ]
|
||||||
|
func parseContainerCommandsFromStr(line string) []string {
|
||||||
|
if line == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
var commands []string
|
||||||
|
if strings.Contains(line, ",") {
|
||||||
|
line = strings.TrimSpace(strings.Trim(line, "[]"))
|
||||||
|
commands = strings.Split(line, ",")
|
||||||
|
// remove space "'
|
||||||
|
for i := range commands {
|
||||||
|
commands[i] = strings.TrimSpace(strings.Trim(commands[i], `"' `))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
commands = append(commands, line)
|
||||||
|
}
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
|
||||||
// searchHPAValues is useful to check if labels
|
// searchHPAValues is useful to check if labels
|
||||||
// contains any labels related to Horizontal Pod Autoscaler
|
// contains any labels related to Horizontal Pod Autoscaler
|
||||||
func searchHPAValues(labels map[string]string) bool {
|
func searchHPAValues(labels map[string]string) bool {
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/kubernetes/kompose/pkg/testutils"
|
"github.com/kubernetes/kompose/pkg/testutils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
api "k8s.io/api/core/v1"
|
||||||
hpa "k8s.io/api/autoscaling/v2beta2"
|
hpa "k8s.io/api/autoscaling/v2beta2"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -741,6 +742,212 @@ func TestRemoveEmptyInterfaces(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_parseContainerCommandsFromStr(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
line string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "line command without spaces in between",
|
||||||
|
line: `[ "bundle", "exec", "thin", "-p", "3000" ]`,
|
||||||
|
want: []string{
|
||||||
|
"bundle", "exec", "thin", "-p", "3000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `line command spaces inside ""`,
|
||||||
|
line: `[ " bundle ", " exec ", " thin ", " -p ", "3000" ]`,
|
||||||
|
want: []string{
|
||||||
|
"bundle", "exec", "thin", "-p", "3000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `more use cases for line command spaces inside ""`,
|
||||||
|
line: `[ " bundle ", "exec ", " thin ", " -p ", "3000 " ]`,
|
||||||
|
want: []string{
|
||||||
|
"bundle", "exec", "thin", "-p", "3000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `line command without [] and ""`,
|
||||||
|
line: `bundle exec thin -p 3000`,
|
||||||
|
want: []string{
|
||||||
|
"bundle exec thin -p 3000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := parseContainerCommandsFromStr(tt.line); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("parseContainerCommandsFromStr() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_fillInitContainers(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
template *api.PodTemplateSpec
|
||||||
|
service kobject.ServiceConfig
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []corev1.Container
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Testing init container are generated from labels with ,",
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "name",
|
||||||
|
compose.LabelInitContainerImage: "image",
|
||||||
|
compose.LabelInitContainerCommand: `[ "bundle", "exec", "thin", "-p", "3000" ]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Image: "image",
|
||||||
|
Command: []string{
|
||||||
|
"bundle", "exec", "thin", "-p", "3000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Testing init container are generated from labels without ,",
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "name",
|
||||||
|
compose.LabelInitContainerImage: "image",
|
||||||
|
compose.LabelInitContainerCommand: `bundle exec thin -p 3000`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Image: "image",
|
||||||
|
Command: []string{
|
||||||
|
`bundle exec thin -p 3000`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Testing init container with long command with vars inside and ''`,
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "init-myservice",
|
||||||
|
compose.LabelInitContainerImage: "busybox:1.28",
|
||||||
|
compose.LabelInitContainerCommand: `['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "init-myservice",
|
||||||
|
Image: "busybox:1.28",
|
||||||
|
Command: []string{
|
||||||
|
"sh", "-c", `until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `without image`,
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "init-myservice",
|
||||||
|
compose.LabelInitContainerImage: "",
|
||||||
|
compose.LabelInitContainerCommand: `['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Testing init container without name`,
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "",
|
||||||
|
compose.LabelInitContainerImage: "busybox:1.28",
|
||||||
|
compose.LabelInitContainerCommand: `['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "init-service",
|
||||||
|
Image: "busybox:1.28",
|
||||||
|
Command: []string{
|
||||||
|
"sh", "-c", `until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Testing init container without command`,
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "init-service",
|
||||||
|
compose.LabelInitContainerImage: "busybox:1.28",
|
||||||
|
compose.LabelInitContainerCommand: ``,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "init-service",
|
||||||
|
Image: "busybox:1.28",
|
||||||
|
Command: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Testing init container without command`,
|
||||||
|
args: args{
|
||||||
|
template: &api.PodTemplateSpec{},
|
||||||
|
service: kobject.ServiceConfig{
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.LabelInitContainerName: "init-service",
|
||||||
|
compose.LabelInitContainerImage: "busybox:1.28",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "init-service",
|
||||||
|
Image: "busybox:1.28",
|
||||||
|
Command: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fillInitContainers(tt.args.template, tt.args.service)
|
||||||
|
if !reflect.DeepEqual(tt.args.template.Spec.InitContainers, tt.want) {
|
||||||
|
t.Errorf("Test_fillInitContainers Fail got %v, want %v", tt.args.template.Spec.InitContainers, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_getHpaValue(t *testing.T) {
|
func Test_getHpaValue(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
service *kobject.ServiceConfig
|
service *kobject.ServiceConfig
|
||||||
|
|||||||
@ -340,6 +340,11 @@ os_output="$KOMPOSE_ROOT/script/test/fixtures/resources-lowercase/output-os.yaml
|
|||||||
convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
|
convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
|
||||||
convert::expect_success "$os_cmd" "$os_output" || exit 1
|
convert::expect_success "$os_cmd" "$os_output" || exit 1
|
||||||
|
|
||||||
|
# Test resources to generate initcontainer
|
||||||
|
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/initcontainer/compose.yaml convert --stdout --with-kompose-annotation=false"
|
||||||
|
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/initcontainer/output-k8s.yaml"
|
||||||
|
convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
|
||||||
|
|
||||||
# Test HPA
|
# Test HPA
|
||||||
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/hpa/compose.yaml convert --stdout --with-kompose-annotation=false"
|
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/hpa/compose.yaml convert --stdout --with-kompose-annotation=false"
|
||||||
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/hpa/output-k8s.yaml"
|
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/hpa/output-k8s.yaml"
|
||||||
|
|||||||
8
script/test/fixtures/initcontainer/compose.yaml
vendored
Normal file
8
script/test/fixtures/initcontainer/compose.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
labels:
|
||||||
|
kompose.init.containers.name: "init-myservice"
|
||||||
|
kompose.init.containers.image: "busybox:1.28"
|
||||||
|
kompose.init.containers.command: '["sh", "-c", "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]'
|
||||||
29
script/test/fixtures/initcontainer/output-k8s.yaml
vendored
Normal file
29
script/test/fixtures/initcontainer/output-k8s.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
io.kompose.service: web
|
||||||
|
name: web
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
io.kompose.service: web
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
io.kompose.network/initcontainer-default: "true"
|
||||||
|
io.kompose.service: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: web
|
||||||
|
initContainers:
|
||||||
|
- command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done
|
||||||
|
image: busybox:1.28
|
||||||
|
name: init-myservice
|
||||||
|
restartPolicy: Always
|
||||||
Loading…
Reference in New Issue
Block a user