diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index c75154fb022f0..a205d7d181899 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -176,8 +176,6 @@ core,github.com/Azure/go-amqp/internal/encoding,MIT,Copyright (C) 2017 Kale Blan core,github.com/Azure/go-amqp/internal/frames,MIT,Copyright (C) 2017 Kale Blankenship | Copyright (C) Microsoft Corporation core,github.com/Azure/go-amqp/internal/queue,MIT,Copyright (C) 2017 Kale Blankenship | Copyright (C) Microsoft Corporation core,github.com/Azure/go-amqp/internal/shared,MIT,Copyright (C) 2017 Kale Blankenship | Copyright (C) Microsoft Corporation -core,github.com/Azure/go-ansiterm,MIT,Copyright (c) 2015 Microsoft Corporation -core,github.com/Azure/go-ansiterm/winterm,MIT,Copyright (c) 2015 Microsoft Corporation core,github.com/Azure/go-autorest,Apache-2.0,Copyright 2015 Microsoft Corporation core,github.com/Azure/go-autorest/autorest,Apache-2.0,Copyright 2015 Microsoft Corporation core,github.com/Azure/go-autorest/autorest/adal,Apache-2.0,Copyright 2015 Microsoft Corporation @@ -1916,7 +1914,6 @@ core,github.com/mitchellh/copystructure,MIT,Copyright (c) 2014 Mitchell Hashimot core,github.com/mitchellh/go-homedir,MIT,Copyright (c) 2013 Mitchell Hashimoto core,github.com/mitchellh/go-ps,MIT,Copyright (c) 2014 Mitchell Hashimoto core,github.com/mitchellh/go-testing-interface,MIT,Copyright (c) 2016 Mitchell Hashimoto -core,github.com/mitchellh/go-wordwrap,MIT,Copyright (c) 2014 Mitchell Hashimoto core,github.com/mitchellh/hashstructure,MIT,Copyright (c) 2016 Mitchell Hashimoto core,github.com/mitchellh/hashstructure/v2,MIT,Copyright (c) 2016 Mitchell Hashimoto core,github.com/mitchellh/mapstructure,MIT,Copyright (c) 2013 Mitchell Hashimoto @@ -1935,8 +1932,6 @@ core,github.com/moby/sys/mountinfo,Apache-2.0,Copyright (c) 2014-2018 The Docker core,github.com/moby/sys/sequential,Apache-2.0,Kir Kolyshkin |Sebastiaan van Stijn |Sebastiaan van Stijn |Tibor Vass |Brian Goff |John Howard |Victor Vieux |Michael Crosby |Daniel Nephin |Tianon Gravi |Vincent Batts |Akihiro Suda |Michael Crosby |Yong Tang |Kir Kolyshkin |Christopher Jones |Guillaume J. Charmes |Kato Kazuyoshi |Manu Gupta |Michael Crosby |Vincent Demeester |Aleksa Sarai |Amit Krishnan |Arnaud Porterie |Brian Goff |Brian Goff |Dan Walsh |Michael Crosby |Phil Estes |Shengjing Zhu |Solomon Hykes |Tobias Klauser |lalyos |unclejack |Akihiro Suda |Alexander Morozov |Jessica Frazelle |Jessica Frazelle |Jessie Frazelle |Justas Brazauskas |Justin Cormack |Kazuyoshi Kato |Naveed Jamil |Vincent Demeester |shuai-z |Ahmet Alp Balkan |Aleksa Sarai |Alexander Larsson |Alexander Morozov |Alexandr Morozov |Alexandr Morozov |Antonio Murdaca |Antonio Murdaca |Antonio Murdaca |Artem Khramov |Cezar Sa Espinola |Chen Hanxiao |Darren Stahl |David Calavera |Derek McGowan |Eng Zer Jun |Erik Dubbelboer |Fabian Kramm |Guillaume Dufour |Guillaume J. Charmes |Hajime Tazaki |Jamie Hannaford |Jason A. Donenfeld |Jhon Honce |Josh Soref |Kasper Fabæch Brandt |Kathryn Baldauf |Kenfe-Mickael Laventure |Kirill Kolyshkin |Muhammad Kaisar Arkhan |Oli |Olli Janatuinen |Paul Nasrat |Peter Bourgon |Peter Waller |Phil Estes |Samuel Karp |Stefan J. Wernli |Steven Hartland |Stig Larsson |Tim Wang |Victor Vieux |Victor Vieux |Yan Feng |jhowardmsft |liuxiaodong |phineas |unclejack |yuexiao-wang |谢致邦 (XIE Zhibang) core,github.com/moby/sys/signal,Apache-2.0,Copyright (c) 2014-2018 The Docker & Go Authors. All rights reserved. core,github.com/moby/sys/user,Apache-2.0,Copyright (c) 2014-2018 The Docker & Go Authors. All rights reserved. -core,github.com/moby/term,Apache-2.0,"Copyright 2013-2018 Docker, Inc | copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons" -core,github.com/moby/term/windows,Apache-2.0,"Copyright 2013-2018 Docker, Inc | copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons" core,github.com/modern-go/concurrent,Apache-2.0,Copyright (c) 2018 Tao Wen core,github.com/modern-go/reflect2,Apache-2.0,Copyright (c) 2018 Tao Wen core,github.com/mohae/deepcopy,MIT,Copyright (c) 2014 Joel @@ -4750,8 +4745,6 @@ core,k8s.io/kube-state-metrics/v2/pkg/watch,Apache-2.0,Copyright 2014 The Kubern core,k8s.io/kubectl/pkg/cmd/util,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/kubectl/pkg/cmd/util/podcmd,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/kubectl/pkg/scheme,Apache-2.0,Copyright 2014 The Kubernetes Authors. -core,k8s.io/kubectl/pkg/util/interrupt,Apache-2.0,Copyright 2014 The Kubernetes Authors. -core,k8s.io/kubectl/pkg/util/term,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/kubelet/pkg/apis/stats/v1alpha1,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/metrics/pkg/apis/custom_metrics,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/metrics/pkg/apis/custom_metrics/install,Apache-2.0,Copyright 2014 The Kubernetes Authors. diff --git a/go.mod b/go.mod index 1700a58548a06..e1e40c8f0d6f6 100644 --- a/go.mod +++ b/go.mod @@ -694,7 +694,6 @@ require ( github.com/jmoiron/sqlx v1.4.0 github.com/judwhite/go-svc v1.2.1 github.com/kr/pretty v0.3.1 - github.com/moby/term v0.5.0 github.com/planetscale/vtprotobuf v0.6.0 github.com/prometheus-community/pro-bing v0.3.0 github.com/rickar/props v1.0.0 @@ -750,7 +749,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 // indirect github.com/Azure/azure-storage-queue-go v0.0.0-20230531184854-c06a8eff66fe // indirect github.com/Azure/go-amqp v1.0.5 // indirect - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect @@ -954,7 +952,6 @@ require ( github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect diff --git a/go.sum b/go.sum index 7b8d4f65837c3..300369dcde1da 100644 --- a/go.sum +++ b/go.sum @@ -1318,8 +1318,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cri-o/ocicni v0.4.3 h1:BfnrZrtr/F+o+b+yOguB1o6I4OzjieF3k3dN4MrsCJA= github.com/cri-o/ocicni v0.4.3/go.mod h1:RzIKSln5AT65hyyfGj3/gsfCpjiY1Y6rVK51Uc5YNzk= github.com/csaf-poc/csaf_distribution/v3 v3.0.0 h1:ob9+Fmpff0YWgTP3dYaw7G2hKQ9cegh9l3zksc+q3sM= @@ -2297,8 +2295,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= diff --git a/pkg/clusteragent/admission/controllers/webhook/controller_v1_test.go b/pkg/clusteragent/admission/controllers/webhook/controller_v1_test.go index 1543b36ef8782..82741c1ea0593 100644 --- a/pkg/clusteragent/admission/controllers/webhook/controller_v1_test.go +++ b/pkg/clusteragent/admission/controllers/webhook/controller_v1_test.go @@ -601,15 +601,7 @@ func TestGenerateTemplatesV1(t *testing.T) { execWebhook := webhook( "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", - &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: cwsinstrumentation.PodLabelEnabled, - Operator: metav1.LabelSelectorOpNotIn, - Values: []string{"false"}, - }, - }, - }, + nil, nil, []admiv1.OperationType{admiv1.Connect}, []string{"pods/exec"}, @@ -646,11 +638,7 @@ func TestGenerateTemplatesV1(t *testing.T) { execWebhook := webhook( "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", - &metav1.LabelSelector{ - MatchLabels: map[string]string{ - cwsinstrumentation.PodLabelEnabled: "true", - }, - }, + nil, nil, []admiv1.OperationType{admiv1.Connect}, []string{"pods/exec"}, @@ -692,15 +680,7 @@ func TestGenerateTemplatesV1(t *testing.T) { "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", nil, - &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: cwsinstrumentation.PodLabelEnabled, - Operator: metav1.LabelSelectorOpNotIn, - Values: []string{"false"}, - }, - }, - }, + nil, []admiv1.OperationType{admiv1.Connect}, []string{"pods/exec"}, ) @@ -737,11 +717,7 @@ func TestGenerateTemplatesV1(t *testing.T) { "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", nil, - &metav1.LabelSelector{ - MatchLabels: map[string]string{ - cwsinstrumentation.PodLabelEnabled: "true", - }, - }, + nil, []admiv1.OperationType{admiv1.Connect}, []string{"pods/exec"}, ) diff --git a/pkg/clusteragent/admission/controllers/webhook/controller_v1beta1_test.go b/pkg/clusteragent/admission/controllers/webhook/controller_v1beta1_test.go index 531c974dc404e..aca1c41ff2453 100644 --- a/pkg/clusteragent/admission/controllers/webhook/controller_v1beta1_test.go +++ b/pkg/clusteragent/admission/controllers/webhook/controller_v1beta1_test.go @@ -594,15 +594,7 @@ func TestGenerateTemplatesV1beta1(t *testing.T) { execWebhook := webhook( "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", - &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: cwsinstrumentation.PodLabelEnabled, - Operator: metav1.LabelSelectorOpNotIn, - Values: []string{"false"}, - }, - }, - }, + nil, nil, []admiv1beta1.OperationType{admiv1beta1.Connect}, []string{"pods/exec"}, @@ -639,11 +631,7 @@ func TestGenerateTemplatesV1beta1(t *testing.T) { execWebhook := webhook( "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", - &metav1.LabelSelector{ - MatchLabels: map[string]string{ - cwsinstrumentation.PodLabelEnabled: "true", - }, - }, + nil, nil, []admiv1beta1.OperationType{admiv1beta1.Connect}, []string{"pods/exec"}, @@ -685,15 +673,7 @@ func TestGenerateTemplatesV1beta1(t *testing.T) { "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", nil, - &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: cwsinstrumentation.PodLabelEnabled, - Operator: metav1.LabelSelectorOpNotIn, - Values: []string{"false"}, - }, - }, - }, + nil, []admiv1beta1.OperationType{admiv1beta1.Connect}, []string{"pods/exec"}, ) @@ -730,11 +710,7 @@ func TestGenerateTemplatesV1beta1(t *testing.T) { "datadog.webhook.cws.exec.instrumentation", "/inject-command-cws", nil, - &metav1.LabelSelector{ - MatchLabels: map[string]string{ - cwsinstrumentation.PodLabelEnabled: "true", - }, - }, + nil, []admiv1beta1.OperationType{admiv1beta1.Connect}, []string{"pods/exec"}, ) diff --git a/pkg/clusteragent/admission/mutate/cwsinstrumentation/cws_instrumentation.go b/pkg/clusteragent/admission/mutate/cwsinstrumentation/cws_instrumentation.go index 854f1b0a1159b..e1f11e39c6161 100644 --- a/pkg/clusteragent/admission/mutate/cwsinstrumentation/cws_instrumentation.go +++ b/pkg/clusteragent/admission/mutate/cwsinstrumentation/cws_instrumentation.go @@ -66,6 +66,7 @@ const ( cwsExcludedResourceReason = "excluded_resource" cwsDescribePodErrorReason = "describe_pod_error" cwsExcludedByAnnotationReason = "excluded_by_annotation" + cwsExcludedByLabelReason = "excluded_by_label" cwsPodNotInstrumentedReason = "pod_not_instrumented" cwsReadonlyFilesystemReason = "readonly_filesystem" cwsMissingArchReason = "missing_arch" @@ -191,8 +192,8 @@ func (w *WebhookForCommands) Operations() []admiv1.OperationType { // LabelSelectors returns the label selectors that specify when the webhook // should be invoked -func (w *WebhookForCommands) LabelSelectors(useNamespaceSelector bool) (namespaceSelector *metav1.LabelSelector, objectSelector *metav1.LabelSelector) { - return labelSelectors(useNamespaceSelector) +func (w *WebhookForCommands) LabelSelectors(_ bool) (namespaceSelector *metav1.LabelSelector, objectSelector *metav1.LabelSelector) { + return nil, nil } // MutateFunc returns the function that mutates the resources @@ -268,6 +269,8 @@ type CWSInstrumentation struct { mode InstrumentationMode // mountVolumeForRemoteCopy mountVolumeForRemoteCopy bool + // directoryForRemoteCopy + directoryForRemoteCopy string // clusterAgentServiceAccount holds the service account name of the cluster agent clusterAgentServiceAccount string @@ -317,6 +320,7 @@ func NewCWSInstrumentation(wmeta workloadmeta.Component) (*CWSInstrumentation, e return nil, fmt.Errorf("can't initiatilize CWS Instrumentation: %v", err) } ci.mountVolumeForRemoteCopy = config.Datadog().GetBool("admission_controller.cws_instrumentation.remote_copy.mount_volume") + ci.directoryForRemoteCopy = config.Datadog().GetString("admission_controller.cws_instrumentation.remote_copy.directory") if ci.mode == RemoteCopy { // build the cluster agent service account @@ -450,6 +454,12 @@ func (ci *CWSInstrumentation) injectCWSCommandInstrumentation(exec *corev1.PodEx return false, errors.New(metrics.InternalError) } + // is the pod excluded explicitly ? (we can filter out with labels in the webhook selector on pods / exec creation) + if pod.Labels != nil && pod.Labels[PodLabelEnabled] == "false" { + metrics.CWSExecInstrumentationAttempts.Observe(1, ci.mode.String(), "false", cwsExcludedByLabelReason) + return false, nil + } + // is the pod targeted by the instrumentation ? if ci.filter.IsExcluded(pod.Annotations, "", "", "") { metrics.CWSExecInstrumentationAttempts.Observe(1, ci.mode.String(), "false", cwsExcludedByAnnotationReason) @@ -469,6 +479,8 @@ func (ci *CWSInstrumentation) injectCWSCommandInstrumentation(exec *corev1.PodEx return false, nil } case RemoteCopy: + cwsInstrumentationRemotePath = filepath.Join(ci.directoryForRemoteCopy, "/cws-instrumentation") + // if we're using a shared volume, we need to make sure the pod is instrumented first if ci.mountVolumeForRemoteCopy { if !isPodCWSInstrumentationReady(pod.Annotations) { @@ -477,7 +489,7 @@ func (ci *CWSInstrumentation) injectCWSCommandInstrumentation(exec *corev1.PodEx metrics.CWSExecInstrumentationAttempts.Observe(1, ci.mode.String(), "false", cwsPodNotInstrumentedReason) return false, nil } - cwsInstrumentationRemotePath = filepath.Join(cwsMountPath, "cws-instrumentation") + cwsInstrumentationRemotePath = filepath.Join(cwsMountPath, cwsInstrumentationRemotePath) } else { // check if the target pod has a read only filesystem if readOnly := ci.hasReadonlyRootfs(pod, exec.Container); readOnly { @@ -486,7 +498,6 @@ func (ci *CWSInstrumentation) injectCWSCommandInstrumentation(exec *corev1.PodEx metrics.CWSExecInstrumentationAttempts.Observe(1, ci.mode.String(), "false", cwsReadonlyFilesystemReason) return false, errors.New(metrics.InvalidInput) } - cwsInstrumentationRemotePath = "/cws-instrumentation" } arch, err := ci.resolveNodeArch(pod.Spec.NodeName, apiClient) @@ -514,7 +525,7 @@ func (ci *CWSInstrumentation) injectCWSCommandInstrumentation(exec *corev1.PodEx // copy CWS instrumentation directly to the target container if err := ci.injectCWSCommandInstrumentationRemoteCopy(pod, container.Name, cwsInstrumentationLocalPath, cwsInstrumentationRemotePath); err != nil { - log.Errorf("Ignoring exec request into %s, remote copy failed: %v", common.PodString(pod), err) + log.Warnf("Ignoring exec request into %s, remote copy failed: %v", common.PodString(pod), err) metrics.CWSExecInstrumentationAttempts.Observe(1, ci.mode.String(), "false", cwsRemoteCopyFailedReason) return false, errors.New(metrics.InternalError) } @@ -771,10 +782,6 @@ func mutatePodExecOptions(rawPodExecOptions []byte, name string, ns string, muta return nil, fmt.Errorf("failed to decode raw object: %v", err) } - if _, err := m(&exec, name, ns, userInfo, dc, apiClient); err != nil { - return nil, err - } - if injected, err := m(&exec, name, ns, userInfo, dc, apiClient); err != nil { metrics.MutationAttempts.Inc(mutationType, metrics.StatusError, strconv.FormatBool(injected), err.Error()) } else { diff --git a/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/cp.go b/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/cp.go index 445be0542077c..3b2ece80c5180 100644 --- a/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/cp.go +++ b/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/cp.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "os" + "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -24,22 +25,26 @@ import ( "k8s.io/kubectl/pkg/scheme" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver" - "github.com/DataDog/datadog-agent/pkg/util/log" ) var ( // CWSRemoteCopyCommand is the command used to copy cws-instrumentation, arguments are split on purpose // to try to differentiate this kubectl cp from others - CWSRemoteCopyCommand = []string{"tar", "-x", "-m", "-f", "-"} + CWSRemoteCopyCommand = []string{"tar", "-x", "-m", "-f", "-", "--totals"} ) +// StreamOptions contains option for the remote stream +type StreamOptions struct { + Stdin bool + genericiooptions.IOStreams +} + // Copy perform remote copy operations type Copy struct { Container string Namespace string apiClient *apiserver.APIClient - streams genericiooptions.IOStreams in *bytes.Buffer out *bytes.Buffer errOut *bytes.Buffer @@ -47,16 +52,25 @@ type Copy struct { // NewCopy creates a Command instance func NewCopy(apiClient *apiserver.APIClient) *Copy { - ioStreams, in, out, errOut := genericiooptions.NewTestIOStreams() return &Copy{ - streams: ioStreams, - in: in, - out: out, - errOut: errOut, + in: &bytes.Buffer{}, + out: &bytes.Buffer{}, + errOut: &bytes.Buffer{}, apiClient: apiClient, } } +func (o *Copy) prepareCommand(destFile remotePath) []string { + // arguments are split on purpose to try to differentiate this kubectl cp from others + cmdArr := make([]string, len(CWSRemoteCopyCommand)) + copy(cmdArr, CWSRemoteCopyCommand) + destFileDir := destFile.Dir().String() + if len(destFileDir) > 0 { + cmdArr = append(cmdArr, "-C", destFileDir) + } + return cmdArr +} + // CopyToPod copies the provided local file to the provided container func (o *Copy) CopyToPod(localFile string, remoteFile string, pod *corev1.Pod, container string) error { o.Container = container @@ -70,21 +84,16 @@ func (o *Copy) CopyToPod(localFile string, remoteFile string, pod *corev1.Pod, c srcFile := newLocalPath(localFile) destFile := newRemotePath(remoteFile) + tarErrChan := make(chan error, 1) go func(src localPath, dest remotePath, writer io.WriteCloser) { defer writer.Close() if err := makeTar(src, dest, writer); err != nil { - log.Debugf("failed to tar local file: %v", err) + tarErrChan <- fmt.Errorf("failed to tar local file: %v", err) + } else { + tarErrChan <- nil } }(srcFile, destFile, writer) - // arguments are split on purpose to try to differentiate this kubectl cp from others - cmdArr := make([]string, len(CWSRemoteCopyCommand)) - copy(cmdArr, CWSRemoteCopyCommand) - destFileDir := destFile.Dir().String() - if len(destFileDir) > 0 { - cmdArr = append(cmdArr, "-C", destFileDir) - } - streamOptions := StreamOptions{ IOStreams: genericiooptions.IOStreams{ In: reader, @@ -93,66 +102,63 @@ func (o *Copy) CopyToPod(localFile string, remoteFile string, pod *corev1.Pod, c }, Stdin: true, } - return o.execute(pod, cmdArr, streamOptions) -} - -func (o *Copy) execute(pod *corev1.Pod, command []string, streamOptions StreamOptions) error { - // ensure we can recover the terminal while attached - t := streamOptions.SetupTTY() - var sizeQueue remotecommand.TerminalSizeQueue - if t.Raw { - // this call spawns a goroutine to monitor/update the terminal size - sizeQueue = t.MonitorSize(t.GetSize()) + if err := o.execute(pod, o.prepareCommand(destFile), streamOptions); err != nil { + return err + } - // unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is - // true - streamOptions.ErrOut = nil + // close pipe, wait for tar chan to finish and check tar error + _ = reader.Close() + tarErr := <-tarErrChan + if tarErr != nil && len(tarErr.Error()) > 0 { + return tarErr } - fn := func() error { - restClient, err := o.apiClient.RESTClient( - "/api", - &schema.GroupVersion{Version: "v1"}, - serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}, - ) - if err != nil { - return err - } + // check stdout and stderr from tar + outData := o.errOut.String() + o.out.String() + if !strings.HasPrefix(outData, "Total bytes read:") { + return fmt.Errorf("unexpected output: %s", outData) + } + return nil +} - req := restClient.Post(). - Resource("pods"). - Name(pod.Name). - Namespace(pod.Namespace). - SubResource("exec") - req.VersionedParams(&corev1.PodExecOptions{ - Container: o.Container, - Command: command, - Stdin: streamOptions.Stdin, - Stdout: streamOptions.Out != nil, - Stderr: streamOptions.ErrOut != nil, - TTY: t.Raw, - }, scheme.ParameterCodec) - - exec, err := o.apiClient.NewSPDYExecutor( - "/api", - &schema.GroupVersion{Version: "v1"}, - serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}, - "POST", - req.URL(), - ) - if err != nil { - return err - } +func (o *Copy) execute(pod *corev1.Pod, command []string, streamOptions StreamOptions) error { + restClient, err := o.apiClient.RESTClient( + "/api", + &schema.GroupVersion{Version: "v1"}, + serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}, + ) + if err != nil { + return err + } - return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{ - Stdin: streamOptions.In, - Stdout: streamOptions.Out, - Stderr: streamOptions.ErrOut, - Tty: t.Raw, - TerminalSizeQueue: sizeQueue, - }) + req := restClient.Post(). + Resource("pods"). + Name(pod.Name). + Namespace(pod.Namespace). + SubResource("exec") + req.VersionedParams(&corev1.PodExecOptions{ + Container: o.Container, + Command: command, + Stdin: streamOptions.Stdin, + Stdout: streamOptions.Out != nil, + Stderr: streamOptions.ErrOut != nil, + }, scheme.ParameterCodec) + + exec, err := o.apiClient.NewSPDYExecutor( + "/api", + &schema.GroupVersion{Version: "v1"}, + serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}, + "POST", + req.URL(), + ) + if err != nil { + return err } - return t.Safe(fn) + return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{ + Stdin: streamOptions.In, + Stdout: streamOptions.Out, + Stderr: streamOptions.ErrOut, + }) } diff --git a/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/streamOptions.go b/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/streamOptions.go deleted file mode 100644 index 8491fe33e618b..0000000000000 --- a/pkg/clusteragent/admission/mutate/cwsinstrumentation/k8scp/streamOptions.go +++ /dev/null @@ -1,67 +0,0 @@ -// 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 2016-present Datadog, Inc. - -//go:build kubeapiserver - -package k8scp - -import ( - "fmt" - - dockerterm "github.com/moby/term" - "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/kubectl/pkg/util/term" -) - -// StreamOptions contains option for the remote stream -type StreamOptions struct { - Stdin bool - TTY bool - - genericiooptions.IOStreams -} - -// SetupTTY sets up the TTY -func (o *StreamOptions) SetupTTY() term.TTY { - t := term.TTY{ - Out: o.Out, - } - - if !o.Stdin { - // need to nil out o.In to make sure we don't create a stream for stdin - o.In = nil - o.TTY = false - return t - } - - t.In = o.In - if !o.TTY { - return t - } - - if !t.IsTerminalIn() { - o.TTY = false - - if o.ErrOut != nil { - fmt.Fprintln(o.ErrOut, "Unable to use a TTY - input is not a terminal or the right kind of file") - } - - return t - } - - // if we get to here, the user wants to attach stdin, wants a TTY, and o.In is a terminal, so we - // can safely set t.Raw to true - t.Raw = true - - stdin, stdout, _ := dockerterm.StdStreams() - o.In = stdin - t.In = stdin - if o.Out != nil { - o.Out = stdout - t.Out = stdout - } - - return t -} diff --git a/pkg/config/setup/config.go b/pkg/config/setup/config.go index c513de0ea1438..b888de3e58f94 100644 --- a/pkg/config/setup/config.go +++ b/pkg/config/setup/config.go @@ -707,6 +707,7 @@ func InitConfig(config pkgconfigmodel.Config) { config.BindEnv("admission_controller.cws_instrumentation.init_resources.memory") config.BindEnvAndSetDefault("admission_controller.cws_instrumentation.mode", "remote_copy") config.BindEnvAndSetDefault("admission_controller.cws_instrumentation.remote_copy.mount_volume", false) + config.BindEnvAndSetDefault("admission_controller.cws_instrumentation.remote_copy.directory", "/tmp") config.BindEnvAndSetDefault("admission_controller.agent_sidecar.enabled", false) config.BindEnvAndSetDefault("admission_controller.agent_sidecar.provider", "") config.BindEnvAndSetDefault("admission_controller.agent_sidecar.endpoint", "/agentsidecar")