From bcec45a5f104318937f2f997e2e74245632aab34 Mon Sep 17 00:00:00 2001 From: danielstokes Date: Thu, 24 Oct 2024 12:39:03 -0500 Subject: [PATCH] feat: php support without annotations (#104) * feat: php support without annotations - 2 * feat: fix-license-key-injection * revise code around language version * revise code for agents --- src/apm/dotnet_test.go | 2 +- src/apm/helper.go | 30 +++++++++++------- src/apm/java_test.go | 2 +- src/apm/nodejs_test.go | 2 +- src/apm/php.go | 71 +++++++++++++++++++++++------------------- src/apm/php_test.go | 16 +++++----- src/apm/python_test.go | 2 +- src/apm/ruby_test.go | 2 +- 8 files changed, 72 insertions(+), 55 deletions(-) diff --git a/src/apm/dotnet_test.go b/src/apm/dotnet_test.go index f40cf9b5..77e8a6d1 100644 --- a/src/apm/dotnet_test.go +++ b/src/apm/dotnet_test.go @@ -73,9 +73,9 @@ func TestDotnetInjector_Inject(t *testing.T) { {Name: "CORECLR_PROFILER_PATH", Value: "/newrelic-instrumentation/libNewRelicProfiler.so"}, {Name: "CORECLR_NEWRELIC_HOME", Value: "/newrelic-instrumentation"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }}, diff --git a/src/apm/helper.go b/src/apm/helper.go index f5b3cb74..4ffce312 100644 --- a/src/apm/helper.go +++ b/src/apm/helper.go @@ -214,17 +214,7 @@ func (i *baseInjector) validate(inst v1alpha2.Instrumentation) error { return nil } -func (i *baseInjector) injectNewrelicConfig(ctx context.Context, resource v1alpha2.Resource, ns corev1.Namespace, pod corev1.Pod, index int, licenseKeySecretName string) corev1.Pod { - container := &pod.Spec.Containers[index] - - if idx := getIndexOfEnv(container.Env, EnvNewRelicAppName); idx == -1 { - //@todo: how can we do this if multiple injectors need this? - resourceMap := i.createResourceMap(ctx, resource, ns, pod, index) - container.Env = append(container.Env, corev1.EnvVar{ - Name: EnvNewRelicAppName, - Value: chooseServiceName(pod, resourceMap, index), - }) - } +func (i *baseInjector) injectNewrelicLicenseKeyIntoContainer(container *corev1.Container, licenseKeySecretName string) { if idx := getIndexOfEnv(container.Env, EnvNewRelicLicenseKey); idx == -1 { optional := true container.Env = append(container.Env, corev1.EnvVar{ @@ -238,6 +228,24 @@ func (i *baseInjector) injectNewrelicConfig(ctx context.Context, resource v1alph }, }) } +} + +func (i *baseInjector) injectNewrelicConfig(ctx context.Context, resource v1alpha2.Resource, ns corev1.Namespace, pod corev1.Pod, index int, licenseKeySecret string) corev1.Pod { + i.injectNewrelicEnvConfig(ctx, resource, ns, pod, index) + i.injectNewrelicLicenseKeyIntoContainer(&pod.Spec.Containers[index], licenseKeySecret) + return pod +} + +func (i *baseInjector) injectNewrelicEnvConfig(ctx context.Context, resource v1alpha2.Resource, ns corev1.Namespace, pod corev1.Pod, index int) corev1.Pod { + container := &pod.Spec.Containers[index] + if idx := getIndexOfEnv(container.Env, EnvNewRelicAppName); idx == -1 { + //@todo: how can we do this if multiple injectors need this? + resourceMap := i.createResourceMap(ctx, resource, ns, pod, index) + container.Env = append(container.Env, corev1.EnvVar{ + Name: EnvNewRelicAppName, + Value: chooseServiceName(pod, resourceMap, index), + }) + } if idx := getIndexOfEnv(container.Env, EnvNewRelicLabels); idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ Name: EnvNewRelicLabels, diff --git a/src/apm/java_test.go b/src/apm/java_test.go index 98f0709d..913ce9d4 100644 --- a/src/apm/java_test.go +++ b/src/apm/java_test.go @@ -70,9 +70,9 @@ func TestJavaInjector_Inject(t *testing.T) { Env: []corev1.EnvVar{ {Name: "JAVA_TOOL_OPTIONS", Value: " -javaagent:/newrelic-instrumentation/newrelic-agent.jar"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }}, diff --git a/src/apm/nodejs_test.go b/src/apm/nodejs_test.go index 37928675..2fe3ba02 100644 --- a/src/apm/nodejs_test.go +++ b/src/apm/nodejs_test.go @@ -70,9 +70,9 @@ func TestNodejsInjector_Inject(t *testing.T) { Env: []corev1.EnvVar{ {Name: "NODE_OPTIONS", Value: " --require /newrelic-instrumentation/newrelicinstrumentation.js"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }}, diff --git a/src/apm/php.go b/src/apm/php.go index f0620d17..aaf6c71e 100644 --- a/src/apm/php.go +++ b/src/apm/php.go @@ -18,14 +18,12 @@ package apm import ( "context" "errors" - corev1 "k8s.io/api/core/v1" "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" ) const ( - annotationPhpVersion = "instrumentation.newrelic.com/php-version" envIniScanDirKey = "PHP_INI_SCAN_DIR" envIniScanDirVal = "/newrelic-instrumentation/php-agent/ini" phpInitContainerName = initContainerName + "-php" @@ -34,30 +32,41 @@ const ( var _ Injector = (*PhpInjector)(nil) func init() { - DefaultInjectorRegistry.MustRegister(&PhpInjector{}) + for _, v := range phpAcceptVersions { + DefaultInjectorRegistry.MustRegister(&PhpInjector{acceptVersion: v}) + } } -// Deprecated: phpApiMap is deprecated. Do not use annotations. -var phpApiMap = map[string]string{ - "7.2": "20170718", - "7.3": "20180731", - "7.4": "20190902", - "8.0": "20200930", - "8.1": "20210902", - "8.2": "20220829", - "8.3": "20230831", -} +const ( + php72 acceptVersion = "php-7.2" + php73 acceptVersion = "php-7.3" + php74 acceptVersion = "php-7.4" + php80 acceptVersion = "php-8.0" + php81 acceptVersion = "php-8.1" + php82 acceptVersion = "php-8.2" + php83 acceptVersion = "php-8.3" +) -type PhpInjector struct { - baseInjector +var phpApiMap = map[acceptVersion]string{ + php72: "20170718", + php73: "20180731", + php74: "20190902", + php80: "20200930", + php81: "20210902", + php82: "20220829", + php83: "20230831", } -func (i *PhpInjector) Language() string { - return "php" +var phpAcceptVersions = []acceptVersion{php72, php73, php74, php80, php81, php82, php83} + +type acceptVersion string + +func (al acceptVersion) Language() string { + return string(al) } -func (i *PhpInjector) acceptable(inst v1alpha2.Instrumentation, pod corev1.Pod) bool { - if inst.Spec.Agent.Language != i.Language() { +func (al acceptVersion) acceptable(inst v1alpha2.Instrumentation, pod corev1.Pod) bool { + if inst.Spec.Agent.Language != string(al) { return false } if len(pod.Spec.Containers) == 0 { @@ -66,8 +75,12 @@ func (i *PhpInjector) acceptable(inst v1alpha2.Instrumentation, pod corev1.Pod) return true } +type PhpInjector struct { + baseInjector + acceptVersion +} + // Inject is used to inject the PHP agent. -// @todo: Currently it uses annotations, which should be removed. This should either use a specific image for each php version or the k8s-agents-operator needs to add support for a language version func (i *PhpInjector) Inject(ctx context.Context, inst v1alpha2.Instrumentation, ns corev1.Namespace, pod corev1.Pod) (corev1.Pod, error) { if !i.acceptable(inst, pod) { return pod, nil @@ -78,15 +91,7 @@ func (i *PhpInjector) Inject(ctx context.Context, inst v1alpha2.Instrumentation, firstContainer := 0 - // exit early if we're missing mandatory annotations - // Deprecated: phpVer is deprecated. Do not use annotations. - phpVer, ok := pod.Annotations[annotationPhpVersion] - if !ok { - return pod, errors.New("missing php version annotation") - } - - // Deprecated: apiNum is deprecated. Do not use annotations. - apiNum, ok := phpApiMap[phpVer] + apiNum, ok := phpApiMap[acceptVersion(i.Language())] if !ok { return pod, errors.New("invalid php version") } @@ -121,7 +126,7 @@ func (i *PhpInjector) Inject(ctx context.Context, inst v1alpha2.Instrumentation, }}) } - pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ + initContainer := corev1.Container{ Name: phpInitContainerName, Image: inst.Spec.Agent.Image, Command: []string{"/bin/sh"}, @@ -133,10 +138,12 @@ func (i *PhpInjector) Inject(ctx context.Context, inst v1alpha2.Instrumentation, Name: volumeName, MountPath: "/newrelic-instrumentation", }}, - }) + } + i.injectNewrelicLicenseKeyIntoContainer(&initContainer, inst.Spec.LicenseKeySecret) + pod.Spec.InitContainers = append(pod.Spec.InitContainers, initContainer) } - pod = i.injectNewrelicConfig(ctx, inst.Spec.Resource, ns, pod, firstContainer, inst.Spec.LicenseKeySecret) + pod = i.injectNewrelicEnvConfig(ctx, inst.Spec.Resource, ns, pod, firstContainer) return pod, nil } diff --git a/src/apm/php_test.go b/src/apm/php_test.go index a1c178b9..74afcb10 100644 --- a/src/apm/php_test.go +++ b/src/apm/php_test.go @@ -14,7 +14,7 @@ import ( ) func TestPhpInjector_Language(t *testing.T) { - require.Equal(t, "php", (&PhpInjector{}).Language()) + require.Equal(t, "php", (&PhpInjector{acceptVersion: acceptVersion("php")}).Language()) } func TestPhpInjector_Inject(t *testing.T) { @@ -58,7 +58,7 @@ func TestPhpInjector_Inject(t *testing.T) { {Name: "test"}, }}}, expectedErrStr: "licenseKeySecret must not be blank", - inst: v1alpha2.Instrumentation{Spec: v1alpha2.InstrumentationSpec{Agent: v1alpha2.Agent{Language: "php"}}}, + inst: v1alpha2.Instrumentation{Spec: v1alpha2.InstrumentationSpec{Agent: v1alpha2.Agent{Language: "php-8.3"}}}, }, { name: "a container, instrumentation", @@ -74,15 +74,17 @@ func TestPhpInjector_Inject(t *testing.T) { Env: []corev1.EnvVar{ {Name: "PHP_INI_SCAN_DIR", Value: "/newrelic-instrumentation/php-agent/ini"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }}, InitContainers: []corev1.Container{{ - Name: "newrelic-instrumentation-php", - Env: []corev1.EnvVar{{Name: "PHP_INI_SCAN_DIR", Value: "/newrelic-instrumentation/php-agent/ini"}}, + Name: "newrelic-instrumentation-php", + Env: []corev1.EnvVar{ + {Name: "PHP_INI_SCAN_DIR", Value: "/newrelic-instrumentation/php-agent/ini"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, + }, Command: []string{"/bin/sh"}, Args: []string{"-c", "cp -a /instrumentation/. /newrelic-instrumentation/ && /newrelic-instrumentation/k8s-php-install.sh 20230831 && /newrelic-instrumentation/nr_env_to_ini.sh"}, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, @@ -90,13 +92,13 @@ func TestPhpInjector_Inject(t *testing.T) { Volumes: []corev1.Volume{{Name: "newrelic-instrumentation", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}}, }, }, - inst: v1alpha2.Instrumentation{Spec: v1alpha2.InstrumentationSpec{Agent: v1alpha2.Agent{Language: "php"}, LicenseKeySecret: "newrelic-key-secret"}}, + inst: v1alpha2.Instrumentation{Spec: v1alpha2.InstrumentationSpec{Agent: v1alpha2.Agent{Language: "php-8.3"}, LicenseKeySecret: "newrelic-key-secret"}}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() - i := &PhpInjector{} + i := &PhpInjector{acceptVersion: acceptVersion("php-8.3")} actualPod, err := i.Inject(ctx, test.inst, test.ns, test.pod) errStr := "" if err != nil { diff --git a/src/apm/python_test.go b/src/apm/python_test.go index ecb2cbbf..9fc1c138 100644 --- a/src/apm/python_test.go +++ b/src/apm/python_test.go @@ -70,9 +70,9 @@ func TestPythonInjector_Inject(t *testing.T) { Env: []corev1.EnvVar{ {Name: "PYTHONPATH", Value: "/newrelic-instrumentation"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }}, diff --git a/src/apm/ruby_test.go b/src/apm/ruby_test.go index 643fb525..14d2459f 100644 --- a/src/apm/ruby_test.go +++ b/src/apm/ruby_test.go @@ -70,9 +70,9 @@ func TestRubyInjector_Inject(t *testing.T) { Env: []corev1.EnvVar{ {Name: "RUBYOPT", Value: "-r /newrelic-instrumentation/lib/boot/strap"}, {Name: "NEW_RELIC_APP_NAME", Value: "test"}, - {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, {Name: "NEW_RELIC_LABELS", Value: "operator:auto-injection"}, {Name: "NEW_RELIC_K8S_OPERATOR_ENABLED", Value: "true"}, + {Name: "NEW_RELIC_LICENSE_KEY", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "newrelic-key-secret"}, Key: "new_relic_license_key", Optional: &vtrue}}}, }, VolumeMounts: []corev1.VolumeMount{{Name: "newrelic-instrumentation", MountPath: "/newrelic-instrumentation"}}, }},