Skip to content

Commit

Permalink
Merge pull request #1210 from FabianKramm/master
Browse files Browse the repository at this point in the history
fix: fixes a nil pointer if the pod or container question is not set
  • Loading branch information
FabianKramm authored Nov 6, 2020
2 parents a81c1a3 + 283efa0 commit e069ac5
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 39 deletions.
9 changes: 9 additions & 0 deletions docs/pages/configuration/deployments/helm-charts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
64 changes: 59 additions & 5 deletions docs/pages/fragments/profile-parent.mdx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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).
2 changes: 1 addition & 1 deletion pkg/devspace/deploy/deployer/helm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}

Expand Down
75 changes: 43 additions & 32 deletions pkg/devspace/kubectl/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)])
}
}

Expand All @@ -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)
Expand Down
55 changes: 54 additions & 1 deletion pkg/devspace/kubectl/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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
}
3 changes: 3 additions & 0 deletions pkg/devspace/services/targetselector/target_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit e069ac5

Please sign in to comment.