Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Feature/add proxy settings (#207)
* Add Proxy setting for the Operator
  • Loading branch information
DTMad committed Feb 20, 2020
1 parent 088bec9 commit a7b8d1a
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 33 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Expand Up @@ -3,10 +3,11 @@
## Future

### Features
* Separated the logic for watching the nodes into nodes_controller to handle scaling correctly ([#189](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/189), [#196](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/196))
* Separated the logic for watching the nodes into nodes_controller to handle scaling correctly ([#189](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/189), [#196](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/196))
* Show operator phase in the `status.phase` field of the OneAgent object ([#197](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/197))
* Build ARM64 images for the Operator ([#201](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/201))
* No longer change the OneAgent .spec section to set defaults ([#206](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/206))
* Added a setting to configure a proxy via the CR ([#207](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/207))

### Bug fixes
* Handle sporadic (and benign) race conditions where the error below would appear ([#194](https://github.com/Dynatrace/dynatrace-oneagent-operator/pull/194)),
Expand Down Expand Up @@ -82,7 +83,7 @@

### [v0.4.0](https://github.com/Dynatrace/dynatrace-oneagent-operator/releases/tag/v0.4.0)

* Support automatic configuration of Istio to allow communication from the OneAgent pods to the Dynatrace environment.
* Support automatic configuration of Istio to allow communication from the OneAgent pods to the Dynatrace environment.
* Added support for Kubernetes 1.16

### [v0.3.1](https://github.com/Dynatrace/dynatrace-oneagent-operator/releases/tag/v0.3.1)
Expand Down
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -136,6 +136,11 @@ spec:
# custom: label
# Name of the service account for the OneAgent (optional)
#serviceAccountName: "dynatrace-oneagent"
# Configures a proxy for the Agent, AgentDownload and the Operator (optional)
# Either provide the proxy URL directly at 'value' or create a secret with a field 'proxy' which holds your encrypted proxy URL
#proxy:
# value: https://my-proxy-url.com
# valueFrom: nameOfMyProxySecret
```
Save the snippet to a file or use [./deploy/cr.yaml](https://raw.githubusercontent.com/Dynatrace/dynatrace-oneagent-operator/master/deploy/cr.yaml) from this repository and adjust its values accordingly.
A secret holding tokens for authenticating to the Dynatrace cluster needs to be created upfront.
Expand Down
7 changes: 6 additions & 1 deletion deploy/cr.yaml
Expand Up @@ -56,4 +56,9 @@ spec:
#labels:
# custom: label
# Name of the service account for the OneAgent (optional)
#serviceAccountName: "dynatrace-oneagent"
#serviceAccountName: "dynatrace-oneagent"
# Configures a proxy for the Agent, AgentDownload and the Operator (optional)
# Either provide the proxy URL directly at 'value' or create a secret with a field 'proxy' which holds your encrypted proxy URL
#proxy:
# value: https://my-proxy-url.com
# valueFrom: nameOfMyProxySecret
13 changes: 11 additions & 2 deletions deploy/crds/dynatrace.com_oneagents_crd.yaml
Expand Up @@ -35,12 +35,12 @@ spec:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
Expand Down Expand Up @@ -185,6 +185,15 @@ spec:
Name must be defined by creating a PriorityClass object with that
name. If not specified the setting will be removed from the DaemonSet.'
type: string
proxy:
description: 'Optional: set custom proxy settings either directly or
from a secret with the field ''proxy'''
properties:
value:
type: string
valueFrom:
type: string
type: object
resources:
description: 'Optional: define resources requests and limits for single
pods'
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Expand Up @@ -374,6 +374,7 @@ github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mo
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down Expand Up @@ -565,6 +566,7 @@ github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfm
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw=
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rubenv/sql-migrate v0.0.0-20191025130928-9355dd04f4b3/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
Expand All @@ -578,6 +580,7 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
Expand Down
10 changes: 10 additions & 0 deletions pkg/apis/dynatrace/v1alpha1/oneagent_types.go
Expand Up @@ -95,6 +95,11 @@ type OneAgentSpec struct {
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Labels"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:advanced,urn:alm:descriptor:com.tectonic.ui:text"
Labels map[string]string `json:"labels,omitempty"`
// Optional: set custom proxy settings either directly or from a secret with the field 'proxy'
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Proxy"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:advanced,urn:alm:descriptor:com.tectonic.ui:text"
Proxy *OneAgentProxy `json:"proxy,omitempty"`
}

type OneAgentConditionType string
Expand All @@ -111,6 +116,11 @@ type OneAgentCondition struct {
Message string `json:"message"`
}

type OneAgentProxy struct {
Value string `json:"value,omitempty"`
ValueFrom string `json:"valueFrom,omitempty"`
}

// Possible reasons for ApiToken and PaaSToken conditions.
const (
// ReasonTokenReady is set when a token has passed verifications.
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/dynatrace/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 34 additions & 3 deletions pkg/controller/oneagent/oneagent_controller.go
Expand Up @@ -374,12 +374,17 @@ func newPodSpecForCR(instance *dynatracev1alpha1.OneAgent) corev1.PodSpec {
sa = instance.Spec.ServiceAccountName
}

args := instance.Spec.Args
if instance.Spec.Proxy != nil && (instance.Spec.Proxy.ValueFrom != "" || instance.Spec.Proxy.Value != "") {
args = append(instance.Spec.Args, "--set-proxy=$(https_proxy)")
}

// K8s 1.18+ is expected to drop the "beta.kubernetes.io" labels in favor of "kubernetes.io" which was added on K8s 1.14.
// To support both older and newer K8s versions we use node affinity.

return corev1.PodSpec{
Containers: []corev1.Container{{
Args: instance.Spec.Args,
Args: args,
Env: prepareEnvVars(instance),
Image: img,
ImagePullPolicy: corev1.PullAlways,
Expand Down Expand Up @@ -461,12 +466,13 @@ func newPodSpecForCR(instance *dynatracev1alpha1.OneAgent) corev1.PodSpec {
}

func prepareEnvVars(instance *dynatracev1alpha1.OneAgent) []corev1.EnvVar {
var token, installerURL, skipCert *corev1.EnvVar
var token, installerURL, skipCert, proxy *corev1.EnvVar

reserved := map[string]**corev1.EnvVar{
"ONEAGENT_INSTALLER_TOKEN": &token,
"ONEAGENT_INSTALLER_SCRIPT_URL": &installerURL,
"ONEAGENT_INSTALLER_SKIP_CERT_CHECK": &skipCert,
"https_proxy": &proxy,
}

var envVars []corev1.EnvVar
Expand Down Expand Up @@ -505,7 +511,32 @@ func prepareEnvVars(instance *dynatracev1alpha1.OneAgent) []corev1.EnvVar {
}
}

return append([]corev1.EnvVar{*token, *installerURL, *skipCert}, envVars...)
env := []corev1.EnvVar{*token, *installerURL, *skipCert}

if proxy == nil {
if instance.Spec.Proxy != nil {
if instance.Spec.Proxy.ValueFrom != "" {
env = append(env, corev1.EnvVar{
Name: "https_proxy",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: instance.Spec.Proxy.ValueFrom},
Key: "proxy",
},
},
})
} else if instance.Spec.Proxy.Value != "" {
env = append(env, corev1.EnvVar{
Name: "https_proxy",
Value: instance.Spec.Proxy.Value,
})
}
}
} else {
env = append(env, *proxy)
}

return append(env, envVars...)
}

// deletePods deletes a list of pods
Expand Down
Expand Up @@ -11,13 +11,16 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
DynatracePaasToken = "paasToken"
DynatraceApiToken = "apiToken"
)

var logger = log.Log.WithName("dynatrace.utils")

// DynatraceClientFunc defines handler func for dynatrace client
type DynatraceClientFunc func(rtc client.Client, instance *dynatracev1alpha1.OneAgent) (dtclient.Client, error)

Expand All @@ -34,7 +37,30 @@ func BuildDynatraceClient(rtc client.Client, instance *dynatracev1alpha1.OneAgen
}

// initialize dynatrace client
var certificateValidation = dtclient.SkipCertificateValidation(instance.Spec.SkipCertCheck)
var opts []dtclient.Option
if instance.Spec.SkipCertCheck {
opts = append(opts, dtclient.SkipCertificateValidation(true))
}

p := instance.Spec.Proxy

if p != nil {
if p.ValueFrom != "" {
proxySecret := &corev1.Secret{}
err := rtc.Get(context.TODO(), client.ObjectKey{Namespace: instance.Namespace, Name: p.ValueFrom}, proxySecret)
if err != nil {
logger.Info("Failed to get proxy field within proxy secret!")
} else {
proxyURL, err := extractToken(proxySecret, "proxy")
if err != nil {
return nil, err
}
opts = append(opts, dtclient.Proxy(proxyURL))
}
} else if p.Value != "" {
opts = append(opts, dtclient.Proxy(p.Value))
}
}

apiToken, err := extractToken(secret, DynatraceApiToken)
if err != nil {
Expand All @@ -46,7 +72,7 @@ func BuildDynatraceClient(rtc client.Client, instance *dynatracev1alpha1.OneAgen
return nil, err
}

dtc, err := dtclient.NewClient(instance.Spec.ApiUrl, apiToken, paasToken, certificateValidation)
dtc, err := dtclient.NewClient(instance.Spec.ApiUrl, apiToken, paasToken, opts...)

return dtc, err
}
Expand Down
File renamed without changes.
45 changes: 22 additions & 23 deletions pkg/dtclient/client.go
Expand Up @@ -4,8 +4,8 @@ import (
"crypto/tls"
"errors"
"net/http"
"net/url"
"strings"
"sync"
)

// Client is the interface for the Dynatrace REST API client.
Expand Down Expand Up @@ -102,8 +102,10 @@ func NewClient(url, apiToken, paasToken string, opts ...Option) (Client, error)
apiToken: apiToken,
paasToken: paasToken,

hostCache: make(map[string]hostInfo),
httpClient: http.DefaultClient,
hostCache: make(map[string]hostInfo),
httpClient: &http.Client{
Transport: http.DefaultTransport.(*http.Transport).Clone(),
},
}

for _, opt := range opts {
Expand All @@ -115,31 +117,28 @@ func NewClient(url, apiToken, paasToken string, opts ...Option) (Client, error)
// Option can be passed to NewClient and customizes the created client instance.
type Option func(*dynatraceClient)

var skipCertValidationClient *http.Client
var skipCertValidationOnce sync.Once

// SkipCertificateValidation creates an Option that specifies whether validation of the server's TLS
// certificate should be skipped. The default is false.
func SkipCertificateValidation(skip bool) Option {
return func(c *dynatraceClient) {
if skip {
skipCertValidationOnce.Do(func() {
if t, ok := http.DefaultTransport.(*http.Transport); ok {
t = t.Clone()
if t.TLSClientConfig == nil {
t.TLSClientConfig = &tls.Config{}
}
t.TLSClientConfig.InsecureSkipVerify = true
skipCertValidationClient = &http.Client{Transport: t}
} else {
logger.Info("can't configure client for disable cert certification")
skipCertValidationClient = &http.Client{}
}
})

c.httpClient = skipCertValidationClient
} else {
c.httpClient = http.DefaultClient
t := c.httpClient.Transport.(*http.Transport)
if t.TLSClientConfig == nil {
t.TLSClientConfig = &tls.Config{}
}
t.TLSClientConfig.InsecureSkipVerify = true
}
}
}

func Proxy(proxyURL string) Option {
return func(c *dynatraceClient) {
p, err := url.Parse(proxyURL)
if err != nil {
logger.Info("Could not parse proxy URL!")
return
}
t := c.httpClient.Transport.(*http.Transport)
t.Proxy = http.ProxyURL(p)
}
}

0 comments on commit a7b8d1a

Please sign in to comment.