Skip to content

Commit

Permalink
[legacy] add kubernetes.yml -> kubelet.yaml import logic (DataDog#1550)
Browse files Browse the repository at this point in the history
  • Loading branch information
xvello authored Apr 26, 2018
1 parent f64c297 commit 1d90121
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 9 deletions.
11 changes: 9 additions & 2 deletions cmd/agent/common/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import (
"strconv"
"strings"

"github.com/DataDog/datadog-agent/pkg/config"
"github.com/DataDog/datadog-agent/pkg/legacy"
"github.com/fatih/color"
yaml "gopkg.in/yaml.v2"

"github.com/DataDog/datadog-agent/pkg/config"
"github.com/DataDog/datadog-agent/pkg/legacy"
)

// ImportConfig imports the agent5 configuration into the agent6 yaml config
Expand Down Expand Up @@ -125,6 +126,12 @@ func ImportConfig(oldConfigDir string, newConfigDir string, force bool) error {
fmt.Sprintf("Ignoring %s, old docker check has been deprecated.", color.YellowString(src)),
)
continue
} else if f.Name() == "kubernetes.yaml" {
err := legacy.ImportKubernetesConf(src, filepath.Join(newConfigDir, "conf.d", "kubelet.yaml"), force)
if err != nil {
return err
}
continue
}

if err := copyFile(src, dst, force); err != nil {
Expand Down
18 changes: 12 additions & 6 deletions docs/agent/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,17 @@ Agent 6 currently supports Kubernetes versions 1.7.6 and above. Support for prev
The `kubernetes` integration insights are provided combining:
* The [`kubelet`](https://github.com/DataDog/integrations-core/tree/master/kubelet) check
retrieving metrics from the kubelet
* The `kubernetes_apiserver` check retrieving events and service checks from the apiserver
* The [`kubernetes_apiserver`](https://github.com/DataDog/datadog-agent/tree/master/cmd/agent/dist/conf.d/kubernetes_apiserver.d) check retrieving events and service checks from the apiserver

The `agent import` command (in versions 6.2 and higher) will import settings from the legacy `kubernetes.yaml` configuration, if found. The following options are deprecated:

- API Server credentials (`api_server_url`, `apiserver_client_crt`, `apiserver_client_key`, `apiserver_ca_cert`) please provide a a kubeconfig file to the agent via the `kubernetes_kubeconfig_path` option
- `use_histogram`: please contact support to determine the best alternative for you
- `namespaces`, `namespace_name_regexp`: Agent6 now collects metrics from all available namespaces

The upgrade logic enables the new prometheus metric collection, that is compatible with Kubernetes versions 1.7.6 and up. If you run an older version or want to revert to the cadvisor collection logic, you can set the `cadvisor_port` option back to `4194` (or the port your kubelet exposes cadvisor at).

The [`kubernetes_state` integration](https://github.com/DataDog/integrations-core/tree/master/kubernetes_state) works on both versions of the agent.

### Tagging

Expand All @@ -316,8 +326,6 @@ The following options and tags are deprecated:
- `kube_replicate_controller` is only added if the pod is created by a replication controller,
not systematically. Use the relevant creator tag (`kube_deployment` / `kube_daemon_set`...)

The `kube_service` tagging depends on the `Datadog Cluster Agent`, which is not released yet.

## Autodiscovery

We reworked the [Autodiscovery](https://docs.datadoghq.com/agent/autodiscovery/) system from the ground up to be faster and more reliable.
Expand All @@ -327,9 +335,7 @@ All documented use cases are supported, please contact our support team if you r

### Kubernetes

When using Kubernetes, the Autodiscovery system now sources information from the kubelet, instead of the Docker daemon. This will allow AD
to work without access to the Docker socket, and enable a more consistent experience accross all parts of the agent. The side effect of that
is that templates in Docker labels are not supported when using the kubelet AD listener. Templates in pod annotations still work as intended.
When using Kubernetes, the Autodiscovery system now sources information from the kubelet, instead of the Docker daemon. This will allow AD to work without access to the Docker socket, and enable a more consistent experience accross all parts of the agent. Also, the default behaviour is to source AD templates from pod annotations. You can enable the `docker` config-provider to use container labels, and replace the `kubelet` listener by the `kubelet` one if you need AD on containers running out of pods.

When specifying AD templates in pod annotations, the new annotation name prefix is `ad.datadoghq.com/`. the previous annotation prefix
`service-discovery.datadoghq.com/` is still supported for Agent6 but support will be removed in Agent7.
Expand Down
2 changes: 1 addition & 1 deletion pkg/legacy/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,6 @@ func ImportDockerConf(src, dst string, overwrite bool) error {
config.Datadog.Set("docker_labels_as_tags", dockerLabelAsTags)
}

fmt.Printf("Successfully move information needed from %s into the datadog.yaml (see 'Autodiscovery' section in datadog.yaml.example)\n\n", src)
fmt.Printf("Successfully imported the contents of %s into datadog.yaml (see 'Autodiscovery' section in datadog.yaml.example)\n\n", src)
return nil
}
224 changes: 224 additions & 0 deletions pkg/legacy/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2018 Datadog, Inc.

package legacy

import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/DataDog/datadog-agent/pkg/collector/providers"
"github.com/DataDog/datadog-agent/pkg/config"

yaml "gopkg.in/yaml.v2"
)

const (
warningNewKubeCheck string = "Warning: The Kubernetes integration has been overhauled, please see https://github.com/DataDog/datadog-agent/blob/master/docs/agent/changes.md#kubernetes-support"
deprecationAPIServerCreds string = "please use kubernetes_kubeconfig_path instead"
deprecationHisto string = "please contact support to determine the best alternative for you"
deprecationFiltering string = "Agent6 now collects metrics from all available namespaces"
deprecationTagPrefix string = "please specify mapping per label via kubernetes_pod_labels_as_tags"
deprecationCadvisorPort string = "Agent6 default mode is to collect kubelet metrics via the new prometheus endpoints. On clusters older than 1.7.6, manually set the cadvisor_port setting to enable cadvisor collection"
)

type legacyKubernetesInstance struct {
KubeletPort int `yaml:"kubelet_port"`
KubeletHost string `yaml:"host"`
KubeletClientCrt string `yaml:"kubelet_client_crt"`
KubeletClientKey string `yaml:"kubelet_client_key"`
KubeletCACert string `yaml:"kubelet_cert"`
KubeletTokenPath string `yaml:"bearer_token_path"`
KubeletTLSVerify string `yaml:"kubelet_tls_verify"`
NodeLabelsToTags map[string]string `yaml:"node_labels_to_host_tags"`

CollectEvents bool `yaml:"collect_events"`
LeaderCandidate bool `yaml:"leader_candidate"`
LeaderLeaseDuration int `yaml:"leader_lease_duration"`
CollectServiceTags string `yaml:"collect_service_tags"`
ServiceTagUpdateTag int `yaml:"service_tag_update_freq"`
CadvisorPort string `yaml:"port"`

// Deprecated
APIServerURL string `yaml:"api_server_url"`
APIServerClientCrt string `yaml:"apiserver_client_crt"`
APIServerClientKey string `yaml:"apiserver_client_key"`
APIServerCACert string `yaml:"apiserver_ca_cert"`
Namespaces []string `yaml:"namespaces"`
NamespacesRegexp string `yaml:"namespace_name_regexp"`
UseHisto bool `yaml:"use_histogram"`
LabelTagPrefix string `yaml:"label_to_tag_prefix"`

Tags []string `yaml:"tags"`
}

type newKubeletInstance struct {
CadvisorPort int `yaml:"cadvisor_port"` // will default to 0 == disable
Tags []string `yaml:"tags,omitempty"`
EnabledRates []string `yaml:"enabled_rates,omitempty"`
EnabledGauges []string `yaml:"enabled_gauges,omitempty"`
}

type kubeDeprecations map[string][]string

func (k kubeDeprecations) add(field, message string) {
k[message] = append(k[message], field)
}

func (k kubeDeprecations) print() {
if len(k) == 0 {
return
}
fmt.Println("The following fields are deprecated and not converted:")
for msg, fields := range k {
fmt.Printf(" - %s: %s\n", strings.Join(fields, ", "), msg)
}
}

// ImportKubernetesConf reads the configuration from the kubernetes check (agent5)
// and create the configuration for the new kubelet check (agent 6) and moves
// relevant options to datadog.yaml
func ImportKubernetesConf(src, dst string, overwrite bool) error {
_, err := importKubernetesConfWithDeprec(src, dst, overwrite)
return err
}

// Deprecated options are listed in the kubeDeprecations return value, for testing
func importKubernetesConfWithDeprec(src, dst string, overwrite bool) (kubeDeprecations, error) {
fmt.Printf("%s\n", warningNewKubeCheck)
deprecations := make(kubeDeprecations)

// read kubernetes.yaml
c, err := providers.GetCheckConfigFromFile("kubernetes", src)
if err != nil {
return deprecations, fmt.Errorf("Could not load %s: %s", src, err)
}

if len(c.Instances) == 0 {
return deprecations, nil
}
if len(c.Instances) > 1 {
fmt.Printf("Warning: %s contains more than one instance: converting only the first one", src)
}

// kubelet.yaml (only tags for now)
newKube := &newKubeletInstance{}
if err := yaml.Unmarshal(c.Instances[0], newKube); err != nil {
return deprecations, fmt.Errorf("Could not parse instance from %s: %s", src, err)
}
newCfg := map[string][]*newKubeletInstance{
"instances": {newKube},
}
data, err := yaml.Marshal(newCfg)
if err != nil {
return deprecations, fmt.Errorf("Could not marshall final configuration for the new kubelet check: %s", err)
}
if _, err := os.Stat(dst); !os.IsNotExist(err) {
if overwrite {
// we'll overwrite, backup the original file first
err = os.Rename(dst, dst+".bak")
if err != nil {
return deprecations, fmt.Errorf("unable to create a backup copy of the destination file: %v", err)
}
} else {
return deprecations, fmt.Errorf("destination file already exists, run the command again with --force or -f to overwrite it")
}
}
if err := ioutil.WriteFile(dst, data, 0640); err != nil {
return deprecations, fmt.Errorf("Could not write new kubelet configuration to %s: %s", dst, err)
}
fmt.Printf("Successfully imported the contents of %s into %s\n", src, dst)

// datadog.yaml
instance := &legacyKubernetesInstance{}
if err := yaml.Unmarshal(c.Instances[0], instance); err != nil {
return deprecations, fmt.Errorf("Could not Unmarshal instances from %s: %s", src, err)
}

if instance.KubeletPort > 0 {
config.Datadog.Set("kubernetes_http_kubelet_port", instance.KubeletPort)
config.Datadog.Set("kubernetes_https_kubelet_port", instance.KubeletPort)
}
if len(instance.KubeletHost) > 0 {
config.Datadog.Set("kubernetes_kubelet_host", instance.KubeletHost)
}
if len(instance.KubeletClientCrt) > 0 {
config.Datadog.Set("kubelet_client_crt", instance.KubeletClientCrt)
}
if len(instance.KubeletClientKey) > 0 {
config.Datadog.Set("kubelet_client_key", instance.KubeletClientKey)
}
if len(instance.KubeletCACert) > 0 {
config.Datadog.Set("kubelet_client_ca", instance.KubeletCACert)
}
if len(instance.KubeletTokenPath) > 0 {
config.Datadog.Set("kubelet_auth_token_path", instance.KubeletTokenPath)
}
if len(instance.NodeLabelsToTags) > 0 {
config.Datadog.Set("kubernetes_node_labels_as_tags", instance.NodeLabelsToTags)
}

// We need to verify the kubelet_tls_verify is actually present before
// changing the secure `true` default
if verify, err := strconv.ParseBool(instance.KubeletTLSVerify); err == nil {
config.Datadog.Set("kubelet_tls_verify", verify)
}

// Implicit default in Agent5 was true
if verify, err := strconv.ParseBool(instance.CollectServiceTags); err == nil {
config.Datadog.Set("kubernetes_collect_service_tags", verify)
} else {
config.Datadog.Set("kubernetes_collect_service_tags", true)
}

// Temporarily in main datadog.yaml, will move to DCA
// Booleans are always imported as zero value is false
config.Datadog.Set("collect_kubernetes_events", instance.CollectEvents)
config.Datadog.Set("leader_election", instance.LeaderCandidate)

if instance.LeaderLeaseDuration > 0 {
config.Datadog.Set("leader_lease_duration", instance.LeaderLeaseDuration)
}
if instance.ServiceTagUpdateTag > 0 {
config.Datadog.Set("kubernetes_service_tag_update_freq", instance.ServiceTagUpdateTag)
}

// Deprecations
if len(instance.APIServerURL) > 0 {
deprecations.add("api_server_url", deprecationAPIServerCreds)
}
if len(instance.APIServerClientCrt) > 0 {
deprecations.add("apiserver_client_crt", deprecationAPIServerCreds)
}
if len(instance.APIServerClientKey) > 0 {
deprecations.add("apiserver_client_key", deprecationAPIServerCreds)
}
if len(instance.APIServerCACert) > 0 {
deprecations.add("apiserver_ca_cert", deprecationAPIServerCreds)
}
if instance.UseHisto {
deprecations.add("use_histogram", deprecationHisto)
}
if len(instance.Namespaces) > 0 {
deprecations.add("namespaces", deprecationFiltering)
}
if len(instance.NamespacesRegexp) > 0 {
deprecations.add("namespace_name_regexp", deprecationFiltering)
}
if len(instance.LabelTagPrefix) > 0 {
deprecations.add("label_to_tag_prefix", deprecationTagPrefix)
}
if len(instance.CadvisorPort) > 0 {
deprecations.add("port", deprecationCadvisorPort)
}

deprecations.print()
fmt.Printf("Successfully imported the contents of %s into datadog.yaml\n\n", src)

return deprecations, nil
}
Loading

0 comments on commit 1d90121

Please sign in to comment.