Skip to content

Commit

Permalink
feat: add labels
Browse files Browse the repository at this point in the history
  • Loading branch information
basti1302 committed May 22, 2024
1 parent 6cc1f4e commit e726d73
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 20 deletions.
26 changes: 26 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

operatorv1alpha1 "github.com/dash0hq/dash0-operator/api/v1alpha1"
"github.com/dash0hq/dash0-operator/internal/controller"
"github.com/dash0hq/dash0-operator/internal/k8sresources"
dash0webhook "github.com/dash0hq/dash0-operator/internal/webhook"
//+kubebuilder:scaffold:imports
)
Expand Down Expand Up @@ -137,11 +138,32 @@ func startOperatorManager(
return fmt.Errorf("unable to create the clientset client")
}

operatorVersion, isSet := os.LookupEnv("DASH0_OPERATOR_VERSION")
if !isSet {
operatorVersion = "unknown"
}
initContainerImageVersion, isSet := os.LookupEnv("DASH0_INIT_CONTAINER_IMAGE_VERSION")
if !isSet {
return fmt.Errorf("cannot start Dash0 operator, the mandatory environment variable " +
"\"DASH0_INIT_CONTAINER_IMAGE_VERSION\" is missing")
}
setupLog.Info(
"version information",
"operator version",
operatorVersion,
"init container image version",
initContainerImageVersion,
)

if err = (&controller.Dash0Reconciler{
Client: mgr.GetClient(),
ClientSet: clientSet,
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("dash0-controller"),
Versions: k8sresources.Versions{
OperatorVersion: operatorVersion,
InitContainerImageVersion: initContainerImageVersion,
},
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to set up the Dash0 reconciler: %w", err)
}
Expand All @@ -150,6 +172,10 @@ func startOperatorManager(
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if err = (&dash0webhook.Handler{
Recorder: mgr.GetEventRecorderFor("dash0-webhook"),
Versions: k8sresources.Versions{
OperatorVersion: operatorVersion,
InitContainerImageVersion: initContainerImageVersion,
},
}).SetupWebhookWithManager(mgr); err != nil {
return fmt.Errorf("unable to create the Dash0 webhook: %w", err)
}
Expand Down
5 changes: 5 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ spec:
- --leader-elect
image: dash0-operator-controller:latest
name: manager
env:
- name: DASH0_OPERATOR_VERSION
value: 1.0.0
- name: DASH0_INIT_CONTAINER_IMAGE_VERSION
value: 1.0.0

# Note: Use "imagePullPolicy: Never" when only building the image locally without pushing them anywhere. Omit
# the attribute otherwise to use the default pull policy.
Expand Down
8 changes: 7 additions & 1 deletion internal/controller/dash0_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Dash0Reconciler struct {
ClientSet *kubernetes.Clientset
Scheme *runtime.Scheme
Recorder record.EventRecorder
Versions k8sresources.Versions
}

func (r *Dash0Reconciler) SetupWithManager(mgr ctrl.Manager) error {
Expand Down Expand Up @@ -186,7 +187,12 @@ func (r *Dash0Reconciler) modifyExistingResources(ctx context.Context, dash0Cust
}, &deployment); err != nil {
return fmt.Errorf("error when fetching deployment %s/%s: %w", deployment.GetNamespace(), deployment.GetName(), err)
}
hasBeenModified := k8sresources.ModifyPodSpec(&deployment.Spec.Template.Spec, deployment.GetNamespace(), logger)
hasBeenModified := k8sresources.ModifyDeployment(
&deployment,
deployment.GetNamespace(),
r.Versions,
logger,
)
if hasBeenModified {
return r.Client.Update(ctx, &deployment)
} else {
Expand Down
7 changes: 7 additions & 0 deletions internal/controller/dash0_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"

operatorv1alpha1 "github.com/dash0hq/dash0-operator/api/v1alpha1"
"github.com/dash0hq/dash0-operator/internal/k8sresources"
)

var (
timeout = 15 * time.Second
pollingInterval = 50 * time.Millisecond

versions = k8sresources.Versions{
OperatorVersion: "1.2.3",
InitContainerImageVersion: "4.5.6",
}
)

var _ = Describe("Dash0 Controller", func() {
Expand Down Expand Up @@ -61,6 +67,7 @@ var _ = Describe("Dash0 Controller", func() {
ClientSet: clientset,
Recorder: recorder,
Scheme: k8sClient.Scheme(),
Versions: versions,
}
})

Expand Down
62 changes: 53 additions & 9 deletions internal/k8sresources/modify.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"slices"

"github.com/go-logr/logr"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
initContainerName = "dash0-instrumentation"
initContainerImage = "dash0-instrumentation:1.0.0"
initContainerName = "dash0-instrumentation"
initContainerImageTemplate = "dash0-instrumentation:%s"

dash0VolumeName = "dash0-instrumentation"
dash0DirectoryEnvVarName = "DASH0_INSTRUMENTATION_FOLDER_DESTINATION"
Expand All @@ -28,6 +29,10 @@ const (
envVarNodeOptionsValue = "--require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js"
envVarDash0CollectorBaseUrlName = "DASH0_OTEL_COLLECTOR_BASE_URL"
envVarDash0CollectorBaseUrlNameValueTemplate = "http://dash0-opentelemetry-collector-daemonset.%s.svc.cluster.local:4318"

instrumentedLabelKey = "dash0.instrumented"
operatorVersionLabelKey = "dash0.operator.version"
initContainerImageVersionLabelKey = "dash0.initcontainer.image.version"
)

var (
Expand All @@ -38,14 +43,40 @@ var (
initContainerReadOnlyRootFilesystem = true
)

func ModifyPodSpec(podSpec *corev1.PodSpec, namespace string, logger logr.Logger) bool {
type Versions struct {
OperatorVersion string
InitContainerImageVersion string
}

func ModifyDeployment(
deployment *appsv1.Deployment,
namespace string,
versions Versions,
logger logr.Logger,
) bool {
podTemplateSpec := &deployment.Spec.Template
hasBeenModified := modifyPodSpec(
&podTemplateSpec.Spec,
namespace,
versions.InitContainerImageVersion,
logger,
)
if hasBeenModified {
addInstrumentationLabels(&deployment.ObjectMeta, versions)
addInstrumentationLabels(&podTemplateSpec.ObjectMeta, versions)
}
return hasBeenModified
}

func modifyPodSpec(podSpec *corev1.PodSpec, namespace string, initContainerImageVersion string, logger logr.Logger) bool {
originalSpec := podSpec.DeepCopy()
addInstrumentationVolume(podSpec)
addInitContainer(podSpec)
addInitContainer(podSpec, initContainerImageVersion)
for idx := range podSpec.Containers {
container := &podSpec.Containers[idx]
instrumentContainer(container, namespace, logger)
}

return !reflect.DeepEqual(originalSpec, podSpec)
}

Expand All @@ -72,7 +103,7 @@ func addInstrumentationVolume(podSpec *corev1.PodSpec) {
}
}

func addInitContainer(podSpec *corev1.PodSpec) {
func addInitContainer(podSpec *corev1.PodSpec, initContainerImageVersion string) {
// The init container has all the instrumentation packages (e.g. the Dash0 Node.js distribution etc.), stored under
// /dash0/instrumentation. Its main responsibility is to copy these files to the Kubernetes volume created and mounted in
// addInstrumentationVolume (mounted at /opt/dash0/instrumentation in the init container and also in the target containers).
Expand All @@ -83,15 +114,15 @@ func addInitContainer(podSpec *corev1.PodSpec) {
idx := slices.IndexFunc(podSpec.InitContainers, func(c corev1.Container) bool {
return c.Name == initContainerName
})
initContainer := createInitContainer(podSpec)
initContainer := createInitContainer(podSpec, initContainerImageVersion)
if idx < 0 {
podSpec.InitContainers = append(podSpec.InitContainers, *initContainer)
} else {
podSpec.InitContainers[idx] = *initContainer
}
}

func createInitContainer(podSpec *corev1.PodSpec) *corev1.Container {
func createInitContainer(podSpec *corev1.PodSpec, initContainerImageVersion string) *corev1.Container {
initContainerUser := &defaultInitContainerUser
initContainerGroup := &defaultInitContainerGroup

Expand All @@ -106,7 +137,7 @@ func createInitContainer(podSpec *corev1.PodSpec) *corev1.Container {

return &corev1.Container{
Name: initContainerName,
Image: initContainerImage,
Image: fmt.Sprintf(initContainerImageTemplate, initContainerImageVersion),
Env: []corev1.EnvVar{
{
Name: dash0DirectoryEnvVarName,
Expand Down Expand Up @@ -212,3 +243,16 @@ func addOrReplaceEnvironmentVariable(container *corev1.Container, name string, v
container.Env[idx].Value = value
}
}

func addInstrumentationLabels(meta *v1.ObjectMeta, labelInformation Versions) {
addLabel(meta, instrumentedLabelKey, "true")
addLabel(meta, operatorVersionLabelKey, labelInformation.OperatorVersion)
addLabel(meta, initContainerImageVersionLabelKey, labelInformation.InitContainerImageVersion)
}

func addLabel(meta *v1.ObjectMeta, key string, value string) {
if meta.Labels == nil {
meta.Labels = make(map[string]string, 1)
}
meta.Labels[key] = value
}
28 changes: 25 additions & 3 deletions internal/k8sresources/modify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,26 @@ import (
// intentional. However, this test should be used for more fine-grained test cases, while dash0_webhook_test.go should
// be used to verify external effects (recording events etc.) that cannot be covered in this test.

var (
versions = Versions{
OperatorVersion: "1.2.3",
InitContainerImageVersion: "4.5.6",
}
)

var _ = Describe("Dash0 Resource Modification", func() {

ctx := context.Background()

Context("when mutating new deployments", func() {
It("should inject Dash into a new basic deployment", func() {
deployment := BasicDeployment(TestNamespaceName, DeploymentName)
result := ModifyPodSpec(&deployment.Spec.Template.Spec, TestNamespaceName, log.FromContext(ctx))
result := ModifyDeployment(
deployment,
TestNamespaceName,
versions,
log.FromContext(ctx),
)

Expect(result).To(BeTrue())
VerifyModifiedDeployment(deployment, DeploymentExpectations{
Expand All @@ -46,7 +58,12 @@ var _ = Describe("Dash0 Resource Modification", func() {

It("should inject Dash into a new deployment that has multiple Containers, and already has Volumes and init Containers", func() {
deployment := DeploymentWithMoreBellsAndWhistles(TestNamespaceName, DeploymentName)
result := ModifyPodSpec(&deployment.Spec.Template.Spec, TestNamespaceName, log.FromContext(ctx))
result := ModifyDeployment(
deployment,
TestNamespaceName,
versions,
log.FromContext(ctx),
)

Expect(result).To(BeTrue())
VerifyModifiedDeployment(deployment, DeploymentExpectations{
Expand Down Expand Up @@ -77,7 +94,12 @@ var _ = Describe("Dash0 Resource Modification", func() {

It("should update existing Dash artifacts in a new deployment", func() {
deployment := DeploymentWithExistingDash0Artifacts(TestNamespaceName, DeploymentName)
result := ModifyPodSpec(&deployment.Spec.Template.Spec, TestNamespaceName, log.FromContext(ctx))
result := ModifyDeployment(
deployment,
TestNamespaceName,
versions,
log.FromContext(ctx),
)

Expect(result).To(BeTrue())
VerifyModifiedDeployment(deployment, DeploymentExpectations{
Expand Down
3 changes: 2 additions & 1 deletion internal/webhook/dash0_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (

type Handler struct {
Recorder record.EventRecorder
Versions k8sresources.Versions
}

func (h *Handler) SetupWebhookWithManager(mgr ctrl.Manager) error {
Expand Down Expand Up @@ -59,7 +60,7 @@ func (h *Handler) Handle(_ context.Context, request admission.Request) admission
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error while parsing the resource: %w", err))
}

hasBeenModified := k8sresources.ModifyPodSpec(&deployment.Spec.Template.Spec, request.Namespace, logger)
hasBeenModified := k8sresources.ModifyDeployment(deployment, request.Namespace, h.Versions, logger)
if !hasBeenModified {
return admission.Allowed("no changes")
}
Expand Down
7 changes: 7 additions & 0 deletions internal/webhook/webhook_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
. "github.com/onsi/gomega"

operatorv1alpha1 "github.com/dash0hq/dash0-operator/api/v1alpha1"
"github.com/dash0hq/dash0-operator/internal/k8sresources"
admissionv1 "k8s.io/api/admission/v1"
apimachineryruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
Expand All @@ -40,6 +41,11 @@ var (
testEnv *envtest.Environment
ctx context.Context
cancel context.CancelFunc

versions = k8sresources.Versions{
OperatorVersion: "1.2.3",
InitContainerImageVersion: "4.5.6",
}
)

func TestWebhook(t *testing.T) {
Expand Down Expand Up @@ -115,6 +121,7 @@ var _ = BeforeSuite(func() {

err = (&Handler{
Recorder: mgr.GetEventRecorderFor("dash0-webhook"),
Versions: versions,
}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

Expand Down
Loading

0 comments on commit e726d73

Please sign in to comment.