diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/events.go | 3 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/play.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 28 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 89 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play_test.go | 254 | ||||
-rw-r--r-- | pkg/spec/config_linux_cgo.go | 2 |
7 files changed, 361 insertions, 18 deletions
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 9efdd1261..a729b84d4 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -139,7 +139,8 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } + case <-r.Context().Done(): + return } - } } diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 7b272f01e..3b6dd106f 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -131,7 +131,6 @@ type RmOptions struct { Force bool Ignore bool Latest bool - Storage bool Volumes bool } diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 2ba369b83..356e6869d 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -24,6 +24,8 @@ type PlayKubeOptions struct { // SeccompProfileRoot - path to a directory containing seccomp // profiles. SeccompProfileRoot string + // ConfigMaps - slice of pathnames to kubernetes configmap YAMLs. + ConfigMaps []string } // PlayKubePod represents a single pod and associated containers created by play kube diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 0107e18c4..ac7523094 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -273,16 +273,6 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { reports := []*entities.RmReport{} - if options.Storage { - for _, ctr := range namesOrIds { - report := entities.RmReport{Id: ctr} - if err := ic.Libpod.RemoveStorageContainer(ctr, options.Force); err != nil { - report.Err = err - } - reports = append(reports, &report) - } - return reports, nil - } names := namesOrIds for _, cidFile := range options.CIDFiles { @@ -294,6 +284,22 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, names = append(names, id) } + // Attempt to remove named containers directly from storage, if container is defined in libpod + // this will fail and code will fall through to removing the container from libpod.` + tmpNames := []string{} + for _, ctr := range names { + report := entities.RmReport{Id: ctr} + if err := ic.Libpod.RemoveStorageContainer(ctr, options.Force); err != nil { + // remove container names that we successfully deleted + tmpNames = append(tmpNames, ctr) + } else { + reports = append(reports, &report) + } + } + if len(tmpNames) < len(names) { + names = tmpNames + } + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { // Failed to get containers. If force is specified, get the containers ID @@ -302,7 +308,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return nil, err } - for _, ctr := range namesOrIds { + for _, ctr := range names { logrus.Debugf("Evicting container %q", ctr) report := entities.RmReport{Id: ctr} id, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 40edc1ae3..2de98d8f5 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -311,6 +311,22 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY ctrRestartPolicy = libpod.RestartPolicyAlways } + configMaps := []v1.ConfigMap{} + for _, p := range options.ConfigMaps { + f, err := os.Open(p) + if err != nil { + return nil, err + } + defer f.Close() + + cm, err := readConfigMapFromFile(f) + if err != nil { + return nil, errors.Wrapf(err, "%q", p) + } + + configMaps = append(configMaps, cm) + } + containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) for _, container := range podYAML.Spec.Containers { pullPolicy := util.PullImageMissing @@ -334,7 +350,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - conf, err := kubeContainerToCreateConfig(ctx, container, newImage, namespaces, volumes, pod.ID(), podName, podInfraID, seccompPaths) + conf, err := kubeContainerToCreateConfig(ctx, container, newImage, namespaces, volumes, pod.ID(), podName, podInfraID, configMaps, seccompPaths) if err != nil { return nil, err } @@ -447,7 +463,7 @@ func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfi } // kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container -func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, podName, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) { +func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) { var ( containerConfig createconfig.CreateConfig pidConfig createconfig.PidConfig @@ -572,8 +588,17 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container } envs = imageEnv } - for _, e := range containerYAML.Env { - envs[e.Name] = e.Value + for _, env := range containerYAML.Env { + value := envVarValue(env, configMaps) + + envs[env.Name] = value + } + for _, envFrom := range containerYAML.EnvFrom { + cmEnvs := envVarsFromConfigMap(envFrom, configMaps) + + for k, v := range cmEnvs { + envs[k] = v + } } containerConfig.Env = envs @@ -594,6 +619,62 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container return &containerConfig, nil } +// readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag +func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { + var cm v1.ConfigMap + + content, err := ioutil.ReadAll(r) + if err != nil { + return cm, errors.Wrapf(err, "unable to read ConfigMap YAML content") + } + + if err := yaml.Unmarshal(content, &cm); err != nil { + return cm, errors.Wrapf(err, "unable to read YAML as Kube ConfigMap") + } + + if cm.Kind != "ConfigMap" { + return cm, errors.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", cm.Kind) + } + + return cm, nil +} + +// envVarsFromConfigMap returns all key-value pairs as env vars from a configMap that matches the envFrom setting of a container +func envVarsFromConfigMap(envFrom v1.EnvFromSource, configMaps []v1.ConfigMap) map[string]string { + envs := map[string]string{} + + if envFrom.ConfigMapRef != nil { + cmName := envFrom.ConfigMapRef.Name + + for _, c := range configMaps { + if cmName == c.Name { + envs = c.Data + break + } + } + } + + return envs +} + +// envVarValue returns the environment variable value configured within the container's env setting. +// It gets the value from a configMap if specified, otherwise returns env.Value +func envVarValue(env v1.EnvVar, configMaps []v1.ConfigMap) string { + for _, c := range configMaps { + if env.ValueFrom != nil { + if env.ValueFrom.ConfigMapKeyRef != nil { + if env.ValueFrom.ConfigMapKeyRef.Name == c.Name { + if value, ok := c.Data[env.ValueFrom.ConfigMapKeyRef.Key]; ok { + return value + } + } + } + } + } + + return env.Value +} + // kubeSeccompPaths holds information about a pod YAML's seccomp configuration // it holds both container and pod seccomp paths type kubeSeccompPaths struct { diff --git a/pkg/domain/infra/abi/play_test.go b/pkg/domain/infra/abi/play_test.go new file mode 100644 index 000000000..5595476c3 --- /dev/null +++ b/pkg/domain/infra/abi/play_test.go @@ -0,0 +1,254 @@ +package abi + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var configMapList = []v1.ConfigMap{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + }, + Data: map[string]string{ + "myvar": "bar", + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Data: map[string]string{ + "myvar": "foo", + }, + }, +} + +func TestReadConfigMapFromFile(t *testing.T) { + tests := []struct { + name string + configMapContent string + expectError bool + expectedErrorMsg string + expected v1.ConfigMap + }{ + { + "ValidConfigMap", + ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + myvar: foo +`, + false, + "", + v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Data: map[string]string{ + "myvar": "foo", + }, + }, + }, + { + "InvalidYAML", + ` +Invalid YAML +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + myvar: foo +`, + true, + "unable to read YAML as Kube ConfigMap", + v1.ConfigMap{}, + }, + { + "InvalidKind", + ` +apiVersion: v1 +kind: InvalidKind +metadata: + name: foo +data: + myvar: foo +`, + true, + "invalid YAML kind", + v1.ConfigMap{}, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + buf := bytes.NewBufferString(test.configMapContent) + cm, err := readConfigMapFromFile(buf) + + if test.expectError { + assert.Error(t, err) + assert.Contains(t, err.Error(), test.expectedErrorMsg) + } else { + assert.NoError(t, err) + assert.Equal(t, test.expected, cm) + } + }) + } +} + +func TestEnvVarsFromConfigMap(t *testing.T) { + tests := []struct { + name string + envFrom v1.EnvFromSource + configMapList []v1.ConfigMap + expected map[string]string + }{ + { + "ConfigMapExists", + v1.EnvFromSource{ + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + }, + }, + configMapList, + map[string]string{ + "myvar": "foo", + }, + }, + { + "ConfigMapDoesNotExist", + v1.EnvFromSource{ + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + }, + }, + configMapList, + map[string]string{}, + }, + { + "EmptyConfigMapList", + v1.EnvFromSource{ + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + }, + }, + []v1.ConfigMap{}, + map[string]string{}, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + result := envVarsFromConfigMap(test.envFrom, test.configMapList) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestEnvVarValue(t *testing.T) { + tests := []struct { + name string + envVar v1.EnvVar + configMapList []v1.ConfigMap + expected string + }{ + { + "ConfigMapExists", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "myvar", + }, + }, + }, + configMapList, + "foo", + }, + { + "ContainerKeyDoesNotExistInConfigMap", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "doesnotexist", + }, + }, + }, + configMapList, + "", + }, + { + "ConfigMapDoesNotExist", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "doesnotexist", + }, + Key: "myvar", + }, + }, + }, + configMapList, + "", + }, + { + "EmptyConfigMapList", + v1.EnvVar{ + Name: "FOO", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "foo", + }, + Key: "myvar", + }, + }, + }, + []v1.ConfigMap{}, + "", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + result := envVarValue(test.envVar, test.configMapList) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go index bc8fc4e29..d0891b574 100644 --- a/pkg/spec/config_linux_cgo.go +++ b/pkg/spec/config_linux_cgo.go @@ -39,7 +39,7 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu logrus.Debug("Loading default seccomp profile") seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec) if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) + return nil, errors.Wrapf(err, "loading default seccomp profile failed") } } |