diff --git a/cli/cmd/compose/convert.go b/cli/cmd/compose/convert.go index 0bda501f2..46fb9a8ca 100644 --- a/cli/cmd/compose/convert.go +++ b/cli/cmd/compose/convert.go @@ -116,7 +116,7 @@ func runConvert(ctx context.Context, opts convertOptions, services []string) err } var out io.Writer = os.Stdout - if opts.Output != "" { + if opts.Output != "" && len(json) > 0 { file, err := os.Create(opts.Output) if err != nil { return err diff --git a/kube/client/client.go b/kube/client/client.go index 3f1477c92..2a9b75bb2 100644 --- a/kube/client/client.go +++ b/kube/client/client.go @@ -117,15 +117,13 @@ func (kc *KubeClient) GetLogs(ctx context.Context, projectName string, consumer // WaitForRunningPodState blocks until pods are in running state func (kc KubeClient) WaitForPodState(ctx context.Context, projectName string, services []string, status string, timeout int) error { + var t time.Duration = 60 if timeout > 0 { - var t time.Duration t = time.Duration(timeout) * time.Second - fmt.Println("Timeout ", t) } selector := fmt.Sprintf("%s=%s", compose.ProjectTag, projectName) - waitingForPhase := corev1.PodRunning switch status { @@ -135,49 +133,58 @@ func (kc KubeClient) WaitForPodState(ctx context.Context, projectName string, se waitingForPhase = corev1.PodUnknown } - //fieldSelector := "status.phase=Running" - for { - time.Sleep(time.Duration(1) * time.Second) - timeout = timeout - 1 - if timeout <= 0 { - return fmt.Errorf("Deployment time out. Pods did not reach expected state.") - } + errch := make(chan error, 1) + done := make(chan bool) - pods, err := kc.client.CoreV1().Pods(kc.namespace).List(ctx, metav1.ListOptions{ - LabelSelector: selector, - }) + go func() { + for { + time.Sleep(500 * time.Millisecond) + + pods, err := kc.client.CoreV1().Pods(kc.namespace).List(ctx, metav1.ListOptions{ + LabelSelector: selector, + }) + if err != nil { + errch <- err + } + + servicePods := 0 + stateReached := true + for _, pod := range pods.Items { + + if status == compose.REMOVING { + if contains(services, pod.Labels[compose.ServiceTag]) { + servicePods = servicePods + 1 + } + continue + } + + if pod.Status.Phase != waitingForPhase { + stateReached = false + + } + } + + if status == compose.REMOVING { + if servicePods > 0 { + stateReached = false + } + } + + if stateReached { + done <- true + } + } + }() + + select { + case <-time.After(t): + return fmt.Errorf("timeout: pods did not reach expected state.") + case err := <-errch: if err != nil { return err } - - servicePods := 0 - stateReached := true - for _, pod := range pods.Items { - - if status == compose.REMOVING { - if contains(services, pod.Labels[compose.ServiceTag]) { - servicePods = servicePods + 1 - } - - continue - } - - if pod.Status.Phase != waitingForPhase { - stateReached = false - - } - } - - if status == compose.REMOVING { - if len(pods.Items) > 0 { - continue - } - return nil - } - - if !stateReached { - continue - } + case <-done: return nil } + return nil } diff --git a/kube/compose.go b/kube/compose.go index 7518edc7f..02981b3f4 100644 --- a/kube/compose.go +++ b/kube/compose.go @@ -178,8 +178,8 @@ func (s *composeService) Convert(ctx context.Context, project *types.Project, op } if options.Output != "" { - fullpath, err := helm.SaveChart(chart, options.Output) - return []byte(fullpath), err + _, err := helm.SaveChart(chart, options.Output) + return nil, err } buff := []byte{} diff --git a/kube/resources/kube.go b/kube/resources/kube.go index 20c8fa587..214e9d13b 100644 --- a/kube/resources/kube.go +++ b/kube/resources/kube.go @@ -42,6 +42,17 @@ const ( func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object, error) { objects := map[string]runtime.Object{} + secrets, err := toSecretSpecs(project) + if err != nil { + return nil, err + } + if len(secrets) > 0 { + for _, secret := range secrets { + name := secret.Name[len(project.Name)+1:] + objects[fmt.Sprintf("%s-secret.yaml", name)] = &secret + } + } + for _, service := range project.Services { svcObject := mapToService(project, service) if svcObject != nil { diff --git a/kube/resources/secrets.go b/kube/resources/secrets.go new file mode 100644 index 000000000..3951eb3f9 --- /dev/null +++ b/kube/resources/secrets.go @@ -0,0 +1,58 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package resources + +import ( + "io/ioutil" + "strings" + + "github.com/compose-spec/compose-go/types" + + corev1 "k8s.io/api/core/v1" +) + +func toSecretSpecs(project *types.Project) ([]corev1.Secret, error) { + var secrets []corev1.Secret + + for _, s := range project.Secrets { + if s.External.External { + continue + } + name := strings.ReplaceAll(s.Name, "_", "-") + // load secret file content + sensitiveData, err := ioutil.ReadFile(s.File) + if err != nil { + return nil, err + } + + readOnly := true + secret := corev1.Secret{} + secret.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Secret")) + secret.Name = name + secret.Type = "compose" + secret.Data = map[string][]byte{ + name: sensitiveData, + } + secret.Immutable = &readOnly + + secrets = append(secrets, secret) + } + + return secrets, nil +} diff --git a/kube/resources/volumes.go b/kube/resources/volumes.go index aff187ced..9e2efe220 100644 --- a/kube/resources/volumes.go +++ b/kube/resources/volumes.go @@ -84,16 +84,23 @@ func toVolumeSpecs(project *types.Project, s types.ServiceConfig) ([]volumeSpec, }) } - for i, s := range s.Secrets { - name := fmt.Sprintf("secret-%d", i) + for _, s := range s.Secrets { + name := fmt.Sprintf("%s-%s", project.Name, s.Source) target := path.Join("/run/secrets", or(s.Target, s.Source)) - subPath := name readOnly := true specs = append(specs, volumeSpec{ - source: secretVolume(s, project.Secrets[name], subPath), - mount: volumeMount(name, target, readOnly, subPath), + source: &apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: name, + }, + }, + mount: apiv1.VolumeMount{ + Name: name, + MountPath: target, + ReadOnly: readOnly, + }, }) } @@ -181,7 +188,7 @@ func defaultMode(mode *uint32) *int32 { func secretVolume(config types.ServiceSecretConfig, topLevelConfig types.SecretConfig, subPath string) *apiv1.VolumeSource { return &apiv1.VolumeSource{ Secret: &apiv1.SecretVolumeSource{ - SecretName: config.Source, + SecretName: topLevelConfig.Name, Items: []apiv1.KeyToPath{ { Key: toKey(topLevelConfig.File),