-
Notifications
You must be signed in to change notification settings - Fork 697
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Elastic Agent: Set status.ObservedGeneration from metadata.Generation (
#5383) * Add agent observed generation. Add tests for agent observedGeneration. modify agent reconcile behavior to ensure status updates for certain situations. adjust common comparison to allow additional options Update some documentation. Make disabling of k8s client's errors simpler. Make the status update more readable. Configure initial state for Elastic Agent properly from current state. Add missing comments for some exported data. Make errors consistent with capitalization, and namespace+name. Rename e2e test name to be more descriptive. Update pod labels in e2e test to properly trigger generation update. Remove failing client tests. used keyed structs in tests. * Run generation and update yaml for observedGeneration description change. * Properly order imports * capitalize Agent * remove erroneous debugging line * renamed agent e2e test file. removed erroneous cmp.AllowUnexperted() * Ignore generation when checking if agent status is updated during k8s test steps. remove WithDefaultESValidation from agent generation e2e test. * remove unneeded nil in return from rebase. * Add missing header to e2e test. * Remove unused args struct * move imports around properly * Defer agent validation to reduce repetitive code. Remove unneeded struct in tests. Move Generation e2e tests to it's own package, and use it in all Agent mutation tests. * Updating metav1.Object -> client.Object, and using type switch to get observedGeneration. * Allow status to be updated even if associations are broken. * Allow error to be returned from doReconcile during defer. Updating E2E tests to have similar design as Kibana, and ES E2E tests for generation bits. * ensure withmutatedfrom is used in e2e agent tests. * Ensure we're not returning nil results * Fixing comparison issues from rebase/merge * Add nolint for the naked return, which is required for the defer to function properly. * Handle updating status properly when associations are broken. * Debugging * Debugging, and moving the defer * Go back to previous way of handling fetch association errors * remove extraneous logging statement * Move status update to calling function. Don't worry about status update when FetchWithASsociations fails. Just return errors from updateStatus * Remove unused context * Update the func's comments to be consistent * remove unneeded return * move common.LowestVersionFromPods closer to where pods are retrieved. Update status logic to update health properly. Add unit test for updating of health in agent status. * changes to return status instead of modifying a pointer. * rename variable * Updating how status is handled for consistency
- Loading branch information
Showing
15 changed files
with
394 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
// or more contributor license agreements. Licensed under the Elastic License 2.0; | ||
// you may not use this file except in compliance with the Elastic License 2.0. | ||
|
||
package agent | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
appsv1 "k8s.io/api/apps/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/tools/record" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
|
||
agentv1alpha1 "github.com/elastic/cloud-on-k8s/pkg/apis/agent/v1alpha1" | ||
"github.com/elastic/cloud-on-k8s/pkg/controller/common" | ||
"github.com/elastic/cloud-on-k8s/pkg/controller/common/comparison" | ||
"github.com/elastic/cloud-on-k8s/pkg/controller/common/hash" | ||
"github.com/elastic/cloud-on-k8s/pkg/controller/common/watches" | ||
"github.com/elastic/cloud-on-k8s/pkg/utils/k8s" | ||
"github.com/elastic/cloud-on-k8s/pkg/utils/pointer" | ||
) | ||
|
||
func newReconcileAgent(objs ...runtime.Object) *ReconcileAgent { | ||
r := &ReconcileAgent{ | ||
Client: k8s.NewFakeClient(objs...), | ||
recorder: record.NewFakeRecorder(100), | ||
dynamicWatches: watches.NewDynamicWatches(), | ||
} | ||
return r | ||
} | ||
|
||
func TestReconcileAgent_Reconcile(t *testing.T) { | ||
defaultLabels := NewLabels(agentv1alpha1.Agent{ObjectMeta: metav1.ObjectMeta{Name: "testAgent"}}) | ||
tests := []struct { | ||
name string | ||
objs []runtime.Object | ||
request reconcile.Request | ||
want reconcile.Result | ||
expected agentv1alpha1.Agent | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "valid unmanaged agent does not increment observedGeneration", | ||
objs: []runtime.Object{ | ||
&agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent", | ||
Namespace: "test", | ||
Generation: 1, | ||
Annotations: map[string]string{ | ||
common.ManagedAnnotation: "false", | ||
}, | ||
}, | ||
Spec: agentv1alpha1.AgentSpec{ | ||
Version: "8.0.1", | ||
Deployment: &agentv1alpha1.DeploymentSpec{}, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
ObservedGeneration: 1, | ||
}, | ||
}, | ||
}, | ||
request: reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Namespace: "test", | ||
Name: "testAgent", | ||
}, | ||
}, | ||
want: reconcile.Result{}, | ||
expected: agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent", | ||
Namespace: "test", | ||
Generation: 1, | ||
Annotations: map[string]string{ | ||
common.ManagedAnnotation: "false", | ||
}, | ||
}, | ||
Spec: agentv1alpha1.AgentSpec{ | ||
Version: "8.0.1", | ||
Deployment: &agentv1alpha1.DeploymentSpec{}, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
ObservedGeneration: 1, | ||
}, | ||
}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "too long name fails validation, and updates observedGeneration", | ||
objs: []runtime.Object{ | ||
&agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgentwithtoolongofanamereallylongname", | ||
Namespace: "test", | ||
Generation: 2, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
ObservedGeneration: 1, | ||
}, | ||
}, | ||
}, | ||
request: reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Namespace: "test", | ||
Name: "testAgentwithtoolongofanamereallylongname", | ||
}, | ||
}, | ||
want: reconcile.Result{}, | ||
expected: agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgentwithtoolongofanamereallylongname", | ||
Namespace: "test", | ||
Generation: 2, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
ObservedGeneration: 2, | ||
}, | ||
}, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "agent with ready deployment+pod updates status.health properly", | ||
objs: []runtime.Object{ | ||
&agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent", | ||
Namespace: "test", | ||
Generation: 2, | ||
}, | ||
Spec: agentv1alpha1.AgentSpec{ | ||
Version: "8.0.1", | ||
Deployment: &agentv1alpha1.DeploymentSpec{ | ||
Replicas: pointer.Int32(1), | ||
}, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
ObservedGeneration: 1, | ||
}, | ||
}, | ||
&appsv1.Deployment{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent-agent", | ||
Namespace: "test", | ||
Labels: addLabel(defaultLabels, hash.TemplateHashLabelName, "2519944696"), | ||
}, | ||
Status: appsv1.DeploymentStatus{ | ||
AvailableReplicas: 1, | ||
Replicas: 1, | ||
ReadyReplicas: 1, | ||
Conditions: []appsv1.DeploymentCondition{ | ||
{ | ||
Type: appsv1.DeploymentAvailable, | ||
Status: corev1.ConditionTrue, | ||
}, | ||
}, | ||
}, | ||
}, | ||
&corev1.Pod{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent", | ||
Namespace: "test", | ||
Generation: 2, | ||
Labels: map[string]string{NameLabelName: "testAgent", VersionLabelName: "8.0.1"}, | ||
}, | ||
Status: corev1.PodStatus{ | ||
Phase: corev1.PodRunning, | ||
}, | ||
}, | ||
}, | ||
request: reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Namespace: "test", | ||
Name: "testAgent", | ||
}, | ||
}, | ||
want: reconcile.Result{}, | ||
expected: agentv1alpha1.Agent{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "testAgent", | ||
Namespace: "test", | ||
Generation: 2, | ||
}, | ||
Spec: agentv1alpha1.AgentSpec{ | ||
Version: "8.0.1", | ||
Deployment: &agentv1alpha1.DeploymentSpec{ | ||
Replicas: pointer.Int32(1), | ||
}, | ||
}, | ||
Status: agentv1alpha1.AgentStatus{ | ||
Version: "8.0.1", | ||
ExpectedNodes: 1, | ||
AvailableNodes: 1, | ||
ObservedGeneration: 2, | ||
Health: agentv1alpha1.AgentGreenHealth, | ||
}, | ||
}, | ||
wantErr: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
r := newReconcileAgent(tt.objs...) | ||
got, err := r.Reconcile(context.Background(), tt.request) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("ReconcileAgent.Reconcile() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("ReconcileAgent.Reconcile() = %v, want %v", got, tt.want) | ||
} | ||
|
||
var agent agentv1alpha1.Agent | ||
if err := r.Client.Get(context.Background(), tt.request.NamespacedName, &agent); err != nil { | ||
t.Error(err) | ||
return | ||
} | ||
// AllowUnexported required because of *AssocConf on the agent. | ||
comparison.AssertEqual(t, &agent, &tt.expected, cmp.AllowUnexported(agentv1alpha1.Agent{})) | ||
}) | ||
} | ||
} | ||
|
||
func addLabel(labels map[string]string, key, value string) map[string]string { | ||
newLabels := make(map[string]string, len(labels)) | ||
for k, v := range labels { | ||
newLabels[k] = v | ||
} | ||
newLabels[key] = value | ||
return newLabels | ||
} |
Oops, something went wrong.