Skip to content

Commit

Permalink
feat: php support without annotations (#104)
Browse files Browse the repository at this point in the history
* feat: php support without annotations - 2

* feat: fix-license-key-injection

* revise code around language version

* revise code for agents
  • Loading branch information
danielstokes authored Oct 24, 2024
1 parent 98bd209 commit bcec45a
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/apm/dotnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}},
}},
Expand Down
30 changes: 19 additions & 11 deletions src/apm/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/apm/java_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}},
}},
Expand Down
2 changes: 1 addition & 1 deletion src/apm/nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}},
}},
Expand Down
71 changes: 39 additions & 32 deletions src/apm/php.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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")
}
Expand Down Expand Up @@ -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"},
Expand All @@ -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
}
16 changes: 9 additions & 7 deletions src/apm/php_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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",
Expand All @@ -74,29 +74,31 @@ 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"}},
}},
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 {
Expand Down
2 changes: 1 addition & 1 deletion src/apm/python_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}},
}},
Expand Down
2 changes: 1 addition & 1 deletion src/apm/ruby_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}},
}},
Expand Down

0 comments on commit bcec45a

Please sign in to comment.