From 01e58feea4b22d6960d6d6ef48d55430f3e54e52 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Wed, 4 Nov 2020 09:43:31 +0100 Subject: [PATCH 1/6] fix: fixes a nil pointer if the pod or container question is not set --- pkg/devspace/services/targetselector/target_selector.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/devspace/services/targetselector/target_selector.go b/pkg/devspace/services/targetselector/target_selector.go index 8ee00005e7..6dda5b9196 100644 --- a/pkg/devspace/services/targetselector/target_selector.go +++ b/pkg/devspace/services/targetselector/target_selector.go @@ -61,6 +61,9 @@ func NewTargetSelector(config *latest.Config, kubeClient kubectl.Client, sp *Sel } return &TargetSelector{ + PodQuestion: ptr.String("Please select a pod"), + ContainerQuestion: ptr.String("Please select a container"), + namespace: namespace, labelSelector: labelSelector, imageSelector: imageSelector, From ec2ff885f180ba202f2a2cafe1de91e99fc98a8e Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 6 Nov 2020 08:53:01 +0100 Subject: [PATCH 2/6] feat: try to fallback to in-cluster config if kube config couldn't be loaded --- pkg/devspace/kubectl/util/util.go | 55 ++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/pkg/devspace/kubectl/util/util.go b/pkg/devspace/kubectl/util/util.go index 556b8b6f64..595e3dd78f 100644 --- a/pkg/devspace/kubectl/util/util.go +++ b/pkg/devspace/kubectl/util/util.go @@ -5,10 +5,13 @@ import ( "github.com/devspace-cloud/devspace/pkg/util/kubeconfig" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" ) +const localContext = "local" + func NewClientByContext(context, namespace string, switchContext bool, kubeLoader kubeconfig.Loader) (clientcmd.ClientConfig, string, string, error) { // Load new raw config kubeConfigOriginal, err := kubeLoader.LoadRawConfig() @@ -24,7 +27,18 @@ func NewClientByContext(context, namespace string, switchContext bool, kubeLoade } if len(kubeConfig.Clusters) == 0 { - return nil, "", "", errors.Errorf("kube config is invalid: please make sure you have an existing valid kube config") + // try to load in cluster config + config, err := rest.InClusterConfig() + if err != nil { + return nil, "", "", errors.Errorf("kube config is invalid: please make sure you have an existing valid kube config") + } + + rawConfig, err := ConvertRestConfigToRawConfig(config) + if err != nil { + return nil, "", "", errors.Wrap(err, "convert in cluster config") + } + + return clientcmd.NewNonInteractiveClientConfig(*rawConfig, localContext, &clientcmd.ConfigOverrides{}, clientcmd.NewDefaultClientConfigLoadingRules()), localContext, "default", nil } // If we should use a certain kube context use that @@ -73,3 +87,42 @@ func NewClientByContext(context, namespace string, switchContext bool, kubeLoade return clientConfig, activeContext, activeNamespace, nil } + +func ConvertRestConfigToRawConfig(config *rest.Config) (*clientcmdapi.Config, error) { + contextName := localContext + kubeConfig := clientcmdapi.NewConfig() + kubeConfig.Contexts = map[string]*clientcmdapi.Context{ + contextName: { + Cluster: contextName, + AuthInfo: contextName, + }, + } + kubeConfig.Clusters = map[string]*clientcmdapi.Cluster{ + contextName: { + Server: config.Host, + InsecureSkipTLSVerify: config.Insecure, + CertificateAuthorityData: config.CAData, + CertificateAuthority: config.CAFile, + }, + } + kubeConfig.AuthInfos = map[string]*clientcmdapi.AuthInfo{ + contextName: { + Token: config.BearerToken, + TokenFile: config.BearerTokenFile, + Impersonate: config.Impersonate.UserName, + ImpersonateGroups: config.Impersonate.Groups, + ImpersonateUserExtra: config.Impersonate.Extra, + ClientCertificate: config.CertFile, + ClientCertificateData: config.CertData, + ClientKey: config.KeyFile, + ClientKeyData: config.KeyData, + Username: config.Username, + Password: config.Password, + AuthProvider: config.AuthProvider, + Exec: config.ExecProvider, + }, + } + kubeConfig.CurrentContext = contextName + raw, err := clientcmd.NewDefaultClientConfig(*kubeConfig, &clientcmd.ConfigOverrides{}).RawConfig() + return &raw, err +} From a2227797f2e9079be3cd1da6e4aba8da5f128398 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 6 Nov 2020 09:01:20 +0100 Subject: [PATCH 3/6] chore: bump component chart --- pkg/devspace/deploy/deployer/helm/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/devspace/deploy/deployer/helm/client.go b/pkg/devspace/deploy/deployer/helm/client.go index f831dbcee6..5146dafb56 100644 --- a/pkg/devspace/deploy/deployer/helm/client.go +++ b/pkg/devspace/deploy/deployer/helm/client.go @@ -15,7 +15,7 @@ import ( // DevSpaceChartConfig is the config that holds the devspace chart information var DevSpaceChartConfig = &latest.ChartConfig{ Name: "component-chart", - Version: "0.6.0", + Version: "0.7.0", RepoURL: "https://charts.devspace.sh", } From a93c6bd51df083e4ae59007ef45b55e9fd4ee3cb Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 6 Nov 2020 09:48:28 +0100 Subject: [PATCH 4/6] feat: print logs of all matched containers in pod --- pkg/devspace/kubectl/logs.go | 75 +++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/pkg/devspace/kubectl/logs.go b/pkg/devspace/kubectl/logs.go index bfa4695327..d976bf8a95 100644 --- a/pkg/devspace/kubectl/logs.go +++ b/pkg/devspace/kubectl/logs.go @@ -70,42 +70,40 @@ func (client *client) LogMultipleTimeout(imageSelector []string, interrupt chan // Loop over pods and open logs connection for idx, pod := range pods { - Outer: - for _, container := range pod.Spec.Containers { - for _, imageName := range imageSelector { - if compareImageNames(imageName, container.Image) { - reader, err := client.Logs(ctx, pod.Namespace, pod.Name, container.Name, false, tail, true) - if err != nil { - log.Warnf("Couldn't log %s/%s: %v", pod.Name, container.Name, err) - continue - } + containers := getMatchedContainers(pod, imageSelector) + for idx2, container := range containers { + reader, err := client.Logs(ctx, pod.Namespace, pod.Name, container.Name, false, tail, true) + if err != nil { + log.Warnf("Couldn't log %s/%s: %v", pod.Name, container.Name, err) + continue + } - prefix := pod.Name - if componentLabel, ok := pod.Labels[k8sComponentLabel]; ok { - prefix = componentLabel - } + prefix := pod.Name + if componentLabel, ok := pod.Labels[k8sComponentLabel]; ok { + prefix = componentLabel + } + if len(containers) > 1 { + prefix += ":" + container.Name + } - if printInfo { - log.Info("Starting log streaming for containers that use images defined in devspace.yaml\n") - printInfo = false - } + if printInfo { + log.Info("Starting log streaming for containers that use images defined in devspace.yaml\n") + printInfo = false + } - wg.Add(1) - go func(prefix string, reader io.Reader, color string) { - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - lines <- &logLine{ - line: scanner.Text(), - name: prefix, - color: color, - } - } - - wg.Done() - }(prefix, reader, logpkg.Colors[idx%len(logpkg.Colors)]) - break Outer + wg.Add(1) + go func(prefix string, reader io.Reader, color string) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + lines <- &logLine{ + line: scanner.Text(), + name: prefix, + color: color, + } } - } + + wg.Done() + }(prefix, reader, logpkg.Colors[(idx+idx2)%len(logpkg.Colors)]) } } @@ -127,6 +125,19 @@ func (client *client) LogMultipleTimeout(imageSelector []string, interrupt chan } } +func getMatchedContainers(pod *v1.Pod, imageSelector []string) []v1.Container { + containers := []v1.Container{} + for _, container := range pod.Spec.Containers { + for _, imageName := range imageSelector { + if compareImageNames(imageName, container.Image) { + containers = append(containers, container) + break + } + } + } + return containers +} + // LogMultiple will log multiple func (client *client) LogMultiple(imageSelector []string, interrupt chan error, tail *int64, writer io.Writer, log logpkg.Logger) error { return client.LogMultipleTimeout(imageSelector, interrupt, tail, writer, time.Minute*2, log) From 37b313154ad4e2bfe64aa1c1cf393fce80473765 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 6 Nov 2020 10:06:32 +0100 Subject: [PATCH 5/6] docs: update profile documentation --- docs/pages/fragments/profile-parent.mdx | 64 +++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/docs/pages/fragments/profile-parent.mdx b/docs/pages/fragments/profile-parent.mdx index bdec5b6140..5ff4da20a0 100644 --- a/docs/pages/fragments/profile-parent.mdx +++ b/docs/pages/fragments/profile-parent.mdx @@ -1,11 +1,14 @@ -The `parent` option is optional and expects the name of another profile which should be applied before this profile. The kind of profile inheritance that the `parent` option provides can help to reduce redundancy when multiple profiles need to change the config in a similar way. +The `parents` option is optional and expects the names of other profiles which should be applied before this profile. +The profiles are applied in the order they are specified. It is also possible to apply profiles from distant `devspace.yaml`s. +The kind of profile inheritance that the `parents` option provides can help to reduce redundancy when multiple profiles need to change the config in a similar way. :::info Execution Order -A parent profile is applied before the profile that defines the parent. A parent profile can have a parent of its own. +A parent profile is applied before the profile that defines the parent. A parent profile can have parents of its own. ::: #### Example: Defining a Parent Profile ```yaml {16} +version: v1beta9 images: backend: image: john/devbackend @@ -21,7 +24,8 @@ deployments: - image: john/debugger profiles: - name: production - parent: staging + parents: + - profile: staging patches: - op: add path: deployments.name=backend.helm.values.containers @@ -34,9 +38,59 @@ profiles: image: john/backendprod patches: - op: replace - path: deployments.name=backend.helm.values.container[0].image + path: deployments.name=backend.helm.values.containers[0].image value: john/backendprod - op: remove path: deployments.name=backend.helm.values.containers[1] ``` -When the `production` profile is active, the `replace` and `patches` statements configured in `staging` would be applied first because of the `parent: staging` statement in line 16. After applying the `staging` profile, DevSpace would additionally apply the currently active `production` profile. In this example, the `production` profile is based on the `staging` profile and the only difference is that the `production` profile adds another container to the `backend` deployment which is using the image `john/cache`. +When the `production` profile is active, the `replace` and `patches` statements configured in `staging` would be applied first because of the `parents` statement in line 16. After applying the `staging` profile, DevSpace would additionally apply the currently active `production` profile. In this example, the `production` profile is based on the `staging` profile and the only difference is that the `production` profile adds another container to the `backend` deployment which is using the image `john/cache`. + +#### Example: Defining multiple Parent Profiles +```yaml +version: v1beta9 +profiles: +- name: multi-parents + # Field that applies the profile deployments first and then the profile images + parents: + - profile: deployments + - profile: images +- name: deployments + replace: + deployments: + - name: my-deployment + helm: + componentChart: true + values: + containers: + - name: test +- name: images + replace: + images: + test: + image: mydockeruser/devspace + createPullSecret: true +``` + + +It is also possible to load a profile from a different source with the option `profiles.*.parents.*.source` that can define profiles that lie in a different `devspace.yaml`: + +#### Example: Define a profile from a different devspace.yaml +```yaml +version: v1beta9 +profiles: +- name: dev + parents: + # Will load the profile images from the devspace.yaml specified in other-folder/devspace.yaml + - profile: images + source: + path: other-folder # devspace.yaml will be automatically appended to the path (same syntax as with dependencies) + # or load from url + # path: https://raw.githubusercontent.com/my-org/my-repo/master/devspace.yaml + # or load from git + # git: https://github.com/devspace-cloud/devspace.git + - profile: deployments + source: + path: other-folder-2 +``` + +Under `source` the same options can be specified as for loading [dependencies](../configuration/dependencies/basics.mdx). \ No newline at end of file From 283efa0f62c20219a22f899e08196efe4bbae4e3 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 6 Nov 2020 10:09:59 +0100 Subject: [PATCH 6/6] docs: update helm documentation --- docs/pages/configuration/deployments/helm-charts.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/pages/configuration/deployments/helm-charts.mdx b/docs/pages/configuration/deployments/helm-charts.mdx index fec9c4aa68..52a0531522 100644 --- a/docs/pages/configuration/deployments/helm-charts.mdx +++ b/docs/pages/configuration/deployments/helm-charts.mdx @@ -289,6 +289,15 @@ deployments: ## Helm Options +### `upgradeArgs` +The `upgradeArgs` specifies an array of arguments that will be passed by devspace additionally to the standard arguments to `helm upgrade` during deployment. + +### `templateArgs` +The `templateArgs` specifies an array of arguments that will be passed by devspace additionally to the standard arguments to `helm template` during `devspace render`. + +### `deleteArgs` +The `deleteArgs` specifies an array of arguments that will be passed by devspace additionally to the standard arguments to `helm delete` during `devspace purge`. + ### `wait` The `wait` option expects a boolean that will be used for the [helm flag `--wait`](https://helm.sh/docs/intro/using_helm/#helpful-options-for-installupgraderollback).