Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restart collector pod when config is updated #215

Merged
merged 4 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ rules:
- patch
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- ""
resources:
Expand Down
32 changes: 19 additions & 13 deletions controllers/opentelemetrycollector_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand All @@ -35,10 +36,11 @@ import (
// OpenTelemetryCollectorReconciler reconciles a OpenTelemetryCollector object.
type OpenTelemetryCollectorReconciler struct {
client.Client
log logr.Logger
scheme *runtime.Scheme
config config.Config
tasks []Task
log logr.Logger
scheme *runtime.Scheme
config config.Config
tasks []Task
recorder record.EventRecorder
}

// Task represents a reconciliation task to be executed by the reconciler.
Expand All @@ -51,10 +53,11 @@ type Task struct {
// Params is the set of options to build a new openTelemetryCollectorReconciler.
type Params struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Config config.Config
Tasks []Task
Log logr.Logger
Scheme *runtime.Scheme
Config config.Config
Tasks []Task
Recorder record.EventRecorder
}

// NewReconciler creates a new reconciler for OpenTelemetryCollector objects.
Expand Down Expand Up @@ -95,18 +98,20 @@ func NewReconciler(p Params) *OpenTelemetryCollectorReconciler {
}

return &OpenTelemetryCollectorReconciler{
Client: p.Client,
log: p.Log,
scheme: p.Scheme,
config: p.Config,
tasks: p.Tasks,
Client: p.Client,
log: p.Log,
scheme: p.Scheme,
config: p.Config,
tasks: p.Tasks,
recorder: p.Recorder,
}
}

// +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors/finalizers,verbs=get;update;patch
// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch

// Reconcile the current state of an OpenTelemetry collector resource with the desired state.
func (r *OpenTelemetryCollectorReconciler) Reconcile(_ context.Context, req ctrl.Request) (ctrl.Result, error) {
Expand All @@ -131,6 +136,7 @@ func (r *OpenTelemetryCollectorReconciler) Reconcile(_ context.Context, req ctrl
Instance: instance,
Log: log,
Scheme: r.scheme,
Recorder: r.recorder,
}

if err := r.RunTasks(ctx, params); err != nil {
Expand Down
9 changes: 5 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ func main() {
}

if err = controllers.NewReconciler(controllers.Params{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("OpenTelemetryCollector"),
Scheme: mgr.GetScheme(),
Config: cfg,
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("OpenTelemetryCollector"),
Scheme: mgr.GetScheme(),
Config: cfg,
Recorder: mgr.GetEventRecorderFor("opentelemetry-operator"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "OpenTelemetryCollector")
os.Exit(1)
Expand Down
49 changes: 49 additions & 0 deletions pkg/collector/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector

import (
"crypto/sha256"
"fmt"

"github.com/open-telemetry/opentelemetry-operator/api/v1alpha1"
)

// Annotations return the annotations for OpenTelemetryCollector pod.
func Annotations(instance v1alpha1.OpenTelemetryCollector) map[string]string {
// new map every time, so that we don't touch the instance's annotations
annotations := map[string]string{}

// set default prometheus annotations
annotations["prometheus.io/scrape"] = "true"
annotations["prometheus.io/port"] = "8888"
annotations["prometheus.io/path"] = "/metrics"

// allow override of prometheus annotations
if nil != instance.Annotations {
for k, v := range instance.Annotations {
annotations[k] = v
}
}
// make sure sha256 for configMap is always calculated
annotations["opentelemetry-operator-config/sha256"] = getConfigMapSHA(instance.Spec.Config)

return annotations
}

func getConfigMapSHA(config string) string {
h := sha256.Sum256([]byte(config))
return fmt.Sprintf("%x", h)
}
89 changes: 89 additions & 0 deletions pkg/collector/annotations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector

import (
"testing"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/open-telemetry/opentelemetry-operator/api/v1alpha1"
)

func TestDefaultAnnotations(t *testing.T) {
// prepare
otelcol := v1alpha1.OpenTelemetryCollector{
ObjectMeta: metav1.ObjectMeta{
Name: "my-instance",
Namespace: "my-ns",
},
Spec: v1alpha1.OpenTelemetryCollectorSpec{
Config: "test",
},
}

// test
annotations := Annotations(otelcol)

//verify
assert.Equal(t, "true", annotations["prometheus.io/scrape"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a test for the case where the annotation exists in the CR? If a user has specified scrape as false, we should honor that. I think the code is currently doing the opposite.

Copy link
Contributor Author

@bhiravabhatla bhiravabhatla Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. My bad, fixing it and adding a test for the same

assert.Equal(t, "8888", annotations["prometheus.io/port"])
assert.Equal(t, "/metrics", annotations["prometheus.io/path"])
assert.Equal(t, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", annotations["opentelemetry-operator-config/sha256"])
}

func TestUserAnnotations(t *testing.T) {
// prepare
otelcol := v1alpha1.OpenTelemetryCollector{
ObjectMeta: metav1.ObjectMeta{
Name: "my-instance",
Namespace: "my-ns",
Annotations: map[string]string{"prometheus.io/scrape": "false",
"prometheus.io/port": "1234",
"prometheus.io/path": "/test",
"opentelemetry-operator-config/sha256": "shouldBeOverwritten",
},
},
Spec: v1alpha1.OpenTelemetryCollectorSpec{
Config: "test",
},
}

// test
annotations := Annotations(otelcol)

//verify
assert.Equal(t, "false", annotations["prometheus.io/scrape"])
assert.Equal(t, "1234", annotations["prometheus.io/port"])
assert.Equal(t, "/test", annotations["prometheus.io/path"])
assert.Equal(t, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", annotations["opentelemetry-operator-config/sha256"])
}

func TestAnnotationsPropagateDown(t *testing.T) {
// prepare
otelcol := v1alpha1.OpenTelemetryCollector{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"myapp": "mycomponent"},
},
}

// test
annotations := Annotations(otelcol)

// verify
assert.Len(t, annotations, 5)
assert.Equal(t, "mycomponent", annotations["myapp"])
}
9 changes: 1 addition & 8 deletions pkg/collector/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,7 @@ func DaemonSet(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem
labels := Labels(otelcol)
labels["app.kubernetes.io/name"] = naming.Collector(otelcol)

annotations := otelcol.Annotations
if annotations == nil {
annotations = map[string]string{}
}

annotations["prometheus.io/scrape"] = "true"
annotations["prometheus.io/port"] = "8888"
annotations["prometheus.io/path"] = "/metrics"
annotations := Annotations(otelcol)

return appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Expand Down
9 changes: 1 addition & 8 deletions pkg/collector/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,7 @@ func Deployment(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTele
labels := Labels(otelcol)
labels["app.kubernetes.io/name"] = naming.Collector(otelcol)

annotations := otelcol.Annotations
if annotations == nil {
annotations = map[string]string{}
}

annotations["prometheus.io/scrape"] = "true"
annotations["prometheus.io/port"] = "8888"
annotations["prometheus.io/path"] = "/metrics"
annotations := Annotations(otelcol)

return appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Expand Down
9 changes: 9 additions & 0 deletions pkg/collector/reconcile/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package reconcile
import (
"context"
"fmt"
"reflect"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -124,6 +125,9 @@ func expectedConfigMaps(ctx context.Context, params Params, expected []corev1.Co
if err := params.Client.Patch(ctx, updated, patch); err != nil {
return fmt.Errorf("failed to apply changes: %w", err)
}
if configMapChanged(&desired, existing) {
params.Recorder.Event(updated, "Normal", "ConfigUpdate ", fmt.Sprintf("OpenTelemetry Config changed - %s/%s", desired.Namespace, desired.Name))
jpkrohling marked this conversation as resolved.
Show resolved Hide resolved
}

params.Log.V(2).Info("applied", "configmap.name", desired.Name, "configmap.namespace", desired.Namespace)
}
Expand Down Expand Up @@ -163,3 +167,8 @@ func deleteConfigMaps(ctx context.Context, params Params, expected []corev1.Conf

return nil
}

func configMapChanged(desired *corev1.ConfigMap, actual *corev1.ConfigMap) bool {
return !reflect.DeepEqual(desired.Data, actual.Data)

}
2 changes: 2 additions & 0 deletions pkg/collector/reconcile/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package reconcile
import (
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/open-telemetry/opentelemetry-operator/api/v1alpha1"
Expand All @@ -30,4 +31,5 @@ type Params struct {
Instance v1alpha1.OpenTelemetryCollector
Log logr.Logger
Scheme *runtime.Scheme
Recorder record.EventRecorder
}