diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/libpod/pods.go | 4 | ||||
-rw-r--r-- | pkg/domain/entities/pods.go | 2 | ||||
-rw-r--r-- | pkg/resolvconf/resolvconf.go | 8 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 69 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/play_test.go | 272 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 3 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 2 | ||||
-rw-r--r-- | pkg/specgen/podspecgen.go | 2 |
9 files changed, 284 insertions, 80 deletions
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index afbdf0e5f..d522631b7 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -45,6 +45,10 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { infraOptions.Net = &entities.NetOptions{} infraOptions.Devices = psg.Devices infraOptions.SecurityOpt = psg.SecurityOpt + if psg.ShareParent == nil { + t := true + psg.ShareParent = &t + } err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) if err != nil { utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index aeccc82b4..7922db4e6 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -132,6 +132,7 @@ type PodCreateOptions struct { Name string `json:"name,omitempty"` Net *NetOptions `json:"net,omitempty"` Share []string `json:"share,omitempty"` + ShareParent *bool `json:"share_parent,omitempty"` Pid string `json:"pid,omitempty"` Cpus float64 `json:"cpus,omitempty"` CpusetCpus string `json:"cpuset_cpus,omitempty"` @@ -324,6 +325,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share + s.ShareParent = p.ShareParent s.PodCreateCommand = p.CreateCommand s.VolumesFrom = p.VolumesFrom diff --git a/pkg/resolvconf/resolvconf.go b/pkg/resolvconf/resolvconf.go index d7505e049..f23cd61b0 100644 --- a/pkg/resolvconf/resolvconf.go +++ b/pkg/resolvconf/resolvconf.go @@ -221,9 +221,11 @@ func GetOptions(resolvConf []byte) []string { // dnsSearch, and an "options" entry for every element in dnsOptions. func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) { content := bytes.NewBuffer(nil) - for _, search := range dnsSearch { - if _, err := content.WriteString("search " + search + "\n"); err != nil { - return nil, err + if len(dnsSearch) > 0 { + if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { + if _, err := content.WriteString("search " + searchString + "\n"); err != nil { + return nil, err + } } } for _, dns := range dns { diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index beaec1135..475401016 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "math" "net" "regexp" + "strconv" "strings" "time" @@ -650,6 +652,10 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { if env.ValueFrom.FieldRef != nil { return envVarValueFieldRef(env, opts) } + + if env.ValueFrom.ResourceFieldRef != nil { + return envVarValueResourceFieldRef(env, opts) + } } return &env.Value, nil @@ -688,6 +694,69 @@ func envVarValueFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error ) } +func envVarValueResourceFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { + divisor := env.ValueFrom.ResourceFieldRef.Divisor + if divisor.IsZero() { // divisor not set, use default + divisor.Set(1) + } + + var value *resource.Quantity + resources := opts.Container.Resources + resourceName := env.ValueFrom.ResourceFieldRef.Resource + var isValidDivisor bool + + switch resourceName { + case "limits.memory": + value = resources.Limits.Memory() + isValidDivisor = isMemoryDivisor(divisor) + case "limits.cpu": + value = resources.Limits.Cpu() + isValidDivisor = isCPUDivisor(divisor) + case "requests.memory": + value = resources.Requests.Memory() + isValidDivisor = isMemoryDivisor(divisor) + case "requests.cpu": + value = resources.Requests.Cpu() + isValidDivisor = isCPUDivisor(divisor) + default: + return nil, errors.Errorf( + "Can not set env %v. Reason: resource %v is either not valid or not supported", + env.Name, resourceName, + ) + } + + if !isValidDivisor { + return nil, errors.Errorf( + "Can not set env %s. Reason: divisor value %s is not valid", + env.Name, divisor.String(), + ) + } + + // k8s rounds up the result to the nearest integer + intValue := int(math.Ceil(value.AsApproximateFloat64() / divisor.AsApproximateFloat64())) + stringValue := strconv.Itoa(intValue) + + return &stringValue, nil +} + +func isMemoryDivisor(divisor resource.Quantity) bool { + switch divisor.String() { + case "1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei": + return true + default: + return false + } +} + +func isCPUDivisor(divisor resource.Quantity) bool { + switch divisor.String() { + case "1", "1m": + return true + default: + return false + } +} + // getPodPorts converts a slice of kube container descriptions to an // array of portmapping func getPodPorts(containers []v1.Container) []types.PortMapping { diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go index 2ac268c79..282324310 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -2,13 +2,17 @@ package kube import ( "encoding/json" + "fmt" "io/ioutil" + "math" "os" + "strconv" "testing" "github.com/containers/common/pkg/secrets" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -189,15 +193,13 @@ func TestEnvVarValue(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(d) secretsManager := createSecrets(t, d) - value := "foo" - emptyValue := "" tests := []struct { name string envVar v1.EnvVar options CtrSpecGenOptions succeed bool - expected *string + expected string }{ { "ConfigMapExists", @@ -216,7 +218,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - &value, + "foo", }, { "ContainerKeyDoesNotExistInConfigMap", @@ -235,7 +237,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, false, - nil, + nilString, }, { "OptionalContainerKeyDoesNotExistInConfigMap", @@ -255,7 +257,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - nil, + nilString, }, { "ConfigMapDoesNotExist", @@ -274,7 +276,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, false, - nil, + nilString, }, { "OptionalConfigMapDoesNotExist", @@ -294,7 +296,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: configMapList, }, true, - nil, + nilString, }, { "EmptyConfigMapList", @@ -313,7 +315,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: []v1.ConfigMap{}, }, false, - nil, + nilString, }, { "OptionalEmptyConfigMapList", @@ -333,7 +335,7 @@ func TestEnvVarValue(t *testing.T) { ConfigMaps: []v1.ConfigMap{}, }, true, - nil, + nilString, }, { "SecretExists", @@ -352,7 +354,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - &value, + "foo", }, { "ContainerKeyDoesNotExistInSecret", @@ -371,7 +373,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, false, - nil, + nilString, }, { "OptionalContainerKeyDoesNotExistInSecret", @@ -391,7 +393,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - nil, + nilString, }, { "SecretDoesNotExist", @@ -410,7 +412,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, false, - nil, + nilString, }, { "OptionalSecretDoesNotExist", @@ -430,7 +432,7 @@ func TestEnvVarValue(t *testing.T) { SecretsManager: secretsManager, }, true, - nil, + nilString, }, { "FieldRefMetadataName", @@ -443,10 +445,10 @@ func TestEnvVarValue(t *testing.T) { }, }, CtrSpecGenOptions{ - PodName: value, + PodName: "test", }, true, - &value, + "test", }, { "FieldRefMetadataUID", @@ -459,10 +461,10 @@ func TestEnvVarValue(t *testing.T) { }, }, CtrSpecGenOptions{ - PodID: value, + PodID: "ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977", }, true, - &value, + "ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977", }, { "FieldRefMetadataLabelsExist", @@ -475,10 +477,10 @@ func TestEnvVarValue(t *testing.T) { }, }, CtrSpecGenOptions{ - Labels: map[string]string{"label": value}, + Labels: map[string]string{"label": "label"}, }, true, - &value, + "label", }, { "FieldRefMetadataLabelsEmpty", @@ -494,7 +496,7 @@ func TestEnvVarValue(t *testing.T) { Labels: map[string]string{"label": ""}, }, true, - &emptyValue, + "", }, { "FieldRefMetadataLabelsNotExist", @@ -508,7 +510,7 @@ func TestEnvVarValue(t *testing.T) { }, CtrSpecGenOptions{}, true, - &emptyValue, + "", }, { "FieldRefMetadataAnnotationsExist", @@ -521,10 +523,10 @@ func TestEnvVarValue(t *testing.T) { }, }, CtrSpecGenOptions{ - Annotations: map[string]string{"annotation": value}, + Annotations: map[string]string{"annotation": "annotation"}, }, true, - &value, + "annotation", }, { "FieldRefMetadataAnnotationsEmpty", @@ -540,7 +542,7 @@ func TestEnvVarValue(t *testing.T) { Annotations: map[string]string{"annotation": ""}, }, true, - &emptyValue, + "", }, { "FieldRefMetadataAnnotationsNotExist", @@ -554,7 +556,7 @@ func TestEnvVarValue(t *testing.T) { }, CtrSpecGenOptions{}, true, - &emptyValue, + "", }, { "FieldRefInvalid1", @@ -568,7 +570,7 @@ func TestEnvVarValue(t *testing.T) { }, CtrSpecGenOptions{}, false, - nil, + nilString, }, { "FieldRefInvalid2", @@ -582,7 +584,7 @@ func TestEnvVarValue(t *testing.T) { }, CtrSpecGenOptions{}, false, - nil, + nilString, }, { "FieldRefNotSupported", @@ -596,7 +598,101 @@ func TestEnvVarValue(t *testing.T) { }, CtrSpecGenOptions{}, false, - nil, + nilString, + }, + { + "ResourceFieldRefNotSupported", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.dummy", + }, + }, + }, + CtrSpecGenOptions{}, + false, + nilString, + }, + { + "ResourceFieldRefMemoryDivisorNotValid", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.memory", + Divisor: resource.MustParse("2M"), + }, + }, + }, + CtrSpecGenOptions{}, + false, + nilString, + }, + { + "ResourceFieldRefCpuDivisorNotValid", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.cpu", + Divisor: resource.MustParse("2m"), + }, + }, + }, + CtrSpecGenOptions{}, + false, + nilString, + }, + { + "ResourceFieldRefNoDivisor", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.memory", + }, + }, + }, + CtrSpecGenOptions{ + Container: container, + }, + true, + memoryString, + }, + { + "ResourceFieldRefMemoryDivisor", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "limits.memory", + Divisor: resource.MustParse("1Mi"), + }, + }, + }, + CtrSpecGenOptions{ + Container: container, + }, + true, + strconv.Itoa(int(math.Ceil(float64(memoryInt) / 1024 / 1024))), + }, + { + "ResourceFieldRefCpuDivisor", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ResourceFieldRef: &v1.ResourceFieldSelector{ + Resource: "requests.cpu", + Divisor: resource.MustParse("1m"), + }, + }, + }, + CtrSpecGenOptions{ + Container: container, + }, + true, + strconv.Itoa(int(float64(cpuInt) / 0.001)), }, } @@ -605,59 +701,85 @@ func TestEnvVarValue(t *testing.T) { t.Run(test.name, func(t *testing.T) { result, err := envVarValue(test.envVar, &test.options) assert.Equal(t, err == nil, test.succeed) - assert.Equal(t, test.expected, result) + if test.expected == nilString { + assert.Nil(t, result) + } else { + fmt.Println(*result, test.expected) + assert.Equal(t, &(test.expected), result) + } }) } } -var configMapList = []v1.ConfigMap{ - { - TypeMeta: v12.TypeMeta{ - Kind: "ConfigMap", - }, - ObjectMeta: v12.ObjectMeta{ - Name: "bar", - }, - Data: map[string]string{ - "myvar": "bar", - }, - }, - { - TypeMeta: v12.TypeMeta{ - Kind: "ConfigMap", - }, - ObjectMeta: v12.ObjectMeta{ - Name: "foo", +var ( + nilString = "<nil>" + configMapList = []v1.ConfigMap{ + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "bar", + }, + Data: map[string]string{ + "myvar": "bar", + }, }, - Data: map[string]string{ - "myvar": "foo", + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + Data: map[string]string{ + "myvar": "foo", + }, }, - }, -} + } -var optional = true + optional = true -var k8sSecrets = []v1.Secret{ - { - TypeMeta: v12.TypeMeta{ - Kind: "Secret", - }, - ObjectMeta: v12.ObjectMeta{ - Name: "bar", - }, - Data: map[string][]byte{ - "myvar": []byte("bar"), - }, - }, - { - TypeMeta: v12.TypeMeta{ - Kind: "Secret", + k8sSecrets = []v1.Secret{ + { + TypeMeta: v12.TypeMeta{ + Kind: "Secret", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "bar", + }, + Data: map[string][]byte{ + "myvar": []byte("bar"), + }, }, - ObjectMeta: v12.ObjectMeta{ - Name: "foo", + { + TypeMeta: v12.TypeMeta{ + Kind: "Secret", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + Data: map[string][]byte{ + "myvar": []byte("foo"), + }, }, - Data: map[string][]byte{ - "myvar": []byte("foo"), + } + + cpuInt = 4 + cpuString = strconv.Itoa(cpuInt) + memoryInt = 30000000 + memoryString = strconv.Itoa(memoryInt) + container = v1.Container{ + Name: "test", + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse(cpuString), + v1.ResourceMemory: resource.MustParse(memoryString), + }, + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse(cpuString), + v1.ResourceMemory: resource.MustParse(memoryString), + }, }, - }, -} + } +) diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 93d9caf4c..3f77cbe76 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -482,7 +482,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio for _, toShare := range ns { switch toShare { case "cgroup": - options = append(options, libpod.WithPodCgroups()) + options = append(options, libpod.WithPodCgroup()) case "net": // share the netns setting with other containers in the pod only when it is not set to host if !netnsIsHost { diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 03829e8cf..68fda3ad7 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -166,6 +166,9 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec ) if !p.NoInfra { //&& infraSpec != nil { options = append(options, libpod.WithInfraContainer()) + if p.ShareParent == nil || (p.ShareParent != nil && *p.ShareParent) { + options = append(options, libpod.WithPodParent()) + } nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost()) if err != nil { return nil, err diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index f61937078..e672bc65f 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -57,7 +57,7 @@ const ( // DefaultKernelNamespaces is a comma-separated list of default kernel // namespaces. - DefaultKernelNamespaces = "cgroup,ipc,net,uts" + DefaultKernelNamespaces = "ipc,net,uts" ) // Namespace describes the namespace diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 91b2599cc..759caa0c0 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -63,6 +63,8 @@ type PodBasicConfig struct { // also be used by some tools that wish to recreate the pod // (e.g. `podman generate systemd --new`). // Optional. + // ShareParent determines if all containers in the pod will share the pod's cgroup as the cgroup parent + ShareParent *bool `json:"share_parent,omitempty"` PodCreateCommand []string `json:"pod_create_command,omitempty"` // Pid sets the process id namespace of the pod // Optional (defaults to private if unset). This sets the PID namespace of the infra container |