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

Telemetry webhook validation #482

Closed
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0c18e26
Fix a stray `env:` element that was left hanging after an update toda…
Miles-Garnsey Mar 29, 2022
7a7f683
Refactor telemetry validation out in `pkg/validation` to avoid circul…
Miles-Garnsey Mar 28, 2022
bfd0968
Add telemetry spec validation to webhook.
Miles-Garnsey Mar 28, 2022
8c344b8
Add logic to handle case where CR Datacenters slice is empty.
Miles-Garnsey Mar 29, 2022
c93a7cf
Make stop DC test more timing tolerant.
Miles-Garnsey Mar 29, 2022
9816d92
Rename clientGetter variable in webhook for telemetry validation.
Miles-Garnsey Mar 30, 2022
ffc7a22
Fix nil pointer dereference when spec.datacenters does not exist.
Miles-Garnsey Mar 31, 2022
b1d9250
Another nil dereference fixed.
Miles-Garnsey Mar 31, 2022
9f00a74
Refactor fakeclient and add MockClientCache for remote client test ca…
Miles-Garnsey Mar 31, 2022
9253fe2
Refactor webhook tests out into test/webhooks so that we can use test…
Miles-Garnsey Mar 31, 2022
ad518a5
Fix edge case in telemetry merge logic where assignment to a nil map …
Miles-Garnsey Mar 31, 2022
5237a97
Fix nil pointer dereference in tests due to stargate not being initia…
Miles-Garnsey Mar 31, 2022
ce49d27
Another nil map issue in merge functionality.
Miles-Garnsey Mar 31, 2022
c3e6992
Fix more bugs.
Miles-Garnsey Mar 31, 2022
322d720
Remove debugging code from webhook test and add another unit test.
Miles-Garnsey Mar 31, 2022
c3545fd
Final NPE fixed and unit test added for it.
Miles-Garnsey Apr 1, 2022
8b5df14
Make CreateSingleReaper e2e test timing tolerant.
Miles-Garnsey Apr 1, 2022
347fffc
Make removeDcFromCluster timing tolerant.
Miles-Garnsey Apr 1, 2022
dd62da4
Remove some extraneous logging.
Miles-Garnsey Apr 1, 2022
5e81138
Go fmt and vet changes.
Miles-Garnsey Apr 1, 2022
449aefa
controller-gen copyright update.
Miles-Garnsey Apr 1, 2022
2508ff3
Make CreateSingleDatacenterCluster timing tolerant.
Miles-Garnsey Apr 1, 2022
ce1964e
More async safety for `testRemoveReaperFromK8ssandraCluster()`.
Miles-Garnsey Apr 1, 2022
3c06752
Make fixture deployments retry in e2e tests.
Miles-Garnsey Apr 7, 2022
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
1 change: 0 additions & 1 deletion .github/workflows/test_and_build_image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ jobs:
unit_integration_tests:
name: Run unit/integration tests
runs-on: ubuntu-latest
env:
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
Expand Down
2 changes: 1 addition & 1 deletion apis/config/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 98 additions & 1 deletion apis/k8ssandra/v1alpha1/k8ssandracluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ package v1alpha1
import (
"fmt"

telemetryapi "github.com/k8ssandra/k8ssandra-operator/apis/telemetry/v1alpha1"
"github.com/k8ssandra/k8ssandra-operator/pkg/clientcache"
validationpkg "github.com/k8ssandra/k8ssandra-operator/pkg/validation"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
Expand Down Expand Up @@ -67,6 +70,7 @@ func (r *K8ssandraCluster) ValidateCreate() error {
func (r *K8ssandraCluster) validateK8ssandraCluster() error {
hasClusterStorageConfig := r.Spec.Cassandra.StorageConfig != nil
// Verify given k8s-contexts are correct

for _, dc := range r.Spec.Cassandra.Datacenters {
_, err := clientCache.GetRemoteClient(dc.K8sContext)
if err != nil {
Expand All @@ -85,14 +89,107 @@ func (r *K8ssandraCluster) validateK8ssandraCluster() error {
}
}
}
if err := TelemetrySpecsAreValid(r, clientCache); err != nil {
return err
}
return nil
}

// clientGetter is an interface here purely to enable DI. It is anything that implements `GetRemoteClient()` mechanism to get remote clients by name.
type clientGetter interface {
Miles-Garnsey marked this conversation as resolved.
Show resolved Hide resolved
GetRemoteClient(k8sContextName string) (client.Client, error)
}

func TelemetrySpecsAreValid(kCluster *K8ssandraCluster, cGetter clientGetter) error {
// Iterate through the DCs, checking Stargate and Cassandra telemetry
if kCluster.Spec.Cassandra.Datacenters != nil {
for _, dc := range kCluster.Spec.Cassandra.Datacenters {
//Get client and determine if prom installed.
dcClient, err := cGetter.GetRemoteClient(dc.K8sContext)
if err != nil {
return err
}
promInstalled, err := validationpkg.IsPromInstalled(dcClient, webhookLog)
if err != nil {
return err
}

cassToValidate := &telemetryapi.TelemetrySpec{}
if kCluster.Spec.Cassandra.Telemetry != nil {
cassToValidate = kCluster.Spec.Cassandra.Telemetry.Merge(dc.Telemetry)
} else if dc.Telemetry != nil {
cassToValidate = dc.Telemetry
}
if cassToValidate != nil {
webhookLog.Info("validating cass telemetry in webhook", "cassToValidate", cassToValidate)
cassIsValid, err := validationpkg.TelemetrySpecIsValid(cassToValidate, promInstalled)
if err != nil {
return err
}
if !cassIsValid {
webhookLog.Info("throwing an error, cass telemetry spec invalid")
return errors.New(fmt.Sprint("Cassandra telemetry specification was incorrect in context", dc.K8sContext))
}

}

sgToValidate := &telemetryapi.TelemetrySpec{}
if kCluster.Spec.Stargate != nil && kCluster.Spec.Stargate.Telemetry != nil {
if dc.Stargate != nil {
sgToValidate = kCluster.Spec.Stargate.Telemetry.Merge(dc.Stargate.Telemetry)
} else {
sgToValidate = kCluster.Spec.Stargate.Telemetry
}
} else if dc.Stargate != nil && dc.Stargate.Telemetry != nil {
sgToValidate = dc.Stargate.Telemetry
}
if sgToValidate != nil {
sgIsValid, err := validationpkg.TelemetrySpecIsValid(sgToValidate, promInstalled)
if err != nil {
return err
}
if !sgIsValid {
return errors.New(fmt.Sprint("Stargate telemetry specification was incorrect in context", dc.K8sContext))
}
}
}
}
// Deal with the case that datacenters is completely undefined and the above for loop is empty.
if len(kCluster.Spec.Cassandra.Datacenters) == 0 {
localClient, err := cGetter.GetRemoteClient("")
if err != nil {
return err
}
promInstalled, err := validationpkg.IsPromInstalled(localClient, webhookLog)
if err != nil {
return err
}
if kCluster.Spec.Cassandra.Telemetry != nil {
cassIsValid, err := validationpkg.TelemetrySpecIsValid(kCluster.Spec.Cassandra.Telemetry, promInstalled)
if err != nil {
return err
}
if !cassIsValid {
return errors.New("Cassandra telemetry specification was incorrect in control plane")
}
}
if kCluster.Spec.Stargate != nil && kCluster.Spec.Stargate.Telemetry != nil {
sgIsValid, err := validationpkg.TelemetrySpecIsValid(kCluster.Spec.Stargate.Telemetry, promInstalled)
if err != nil {
return err
}
if !sgIsValid {
return errors.New("Stargate telemetry specification was incorrect in control plane")
}
}

}
return nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *K8ssandraCluster) ValidateUpdate(old runtime.Object) error {
webhookLog.Info("validate K8ssandraCluster update", "K8ssandraCluster", r.Name)

if err := r.validateK8ssandraCluster(); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion apis/k8ssandra/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apis/medusa/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apis/reaper/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apis/replication/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apis/stargate/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions apis/telemetry/v1alpha1/telemetry_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ func (a *PrometheusTelemetrySpec) Merge(b *PrometheusTelemetrySpec) *PrometheusT
}
if a.CommonLabels != nil {
for k, v := range a.CommonLabels {
if out.CommonLabels == nil {
out.CommonLabels = make(map[string]string)
}
out.CommonLabels[k] = v
}
}
if b.CommonLabels != nil {
for k, v := range b.CommonLabels {
if out.CommonLabels == nil {
out.CommonLabels = make(map[string]string)
}
out.CommonLabels[k] = v
}
}
Expand Down
2 changes: 1 addition & 1 deletion apis/telemetry/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions controllers/k8ssandra/cassandra_telemetry_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package k8ssandra
import (
"context"
"errors"
"github.com/k8ssandra/k8ssandra-operator/pkg/validation"

"github.com/go-logr/logr"
cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1"
Expand Down Expand Up @@ -34,11 +35,11 @@ func (r *K8ssandraClusterReconciler) reconcileCassandraDCTelemetry(
}
logger.Info("merged TelemetrySpec constructed", "mergedSpec", mergedSpec, "cluster", kc.Name)
// Confirm telemetry config is valid (e.g. Prometheus is installed if it is requested.)
promInstalled, err := telemetry.IsPromInstalled(remoteClient, logger)
promInstalled, err := validation.IsPromInstalled(remoteClient, logger)
if err != nil {
return result.Error(err)
}
validConfig, err := telemetry.SpecIsValid(mergedSpec, promInstalled)
validConfig, err := validation.TelemetrySpecIsValid(mergedSpec, promInstalled)
jeffbanks marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return result.Error(errors.New("could not determine if telemetry config is valid"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func Test_reconcileCassandraDCTelemetry_TracksNamespaces(t *testing.T) {
// Test fixtures
r := newDummyK8ssandraClusterReconciler()
ctx := context.Background()
fakeClient := test.NewFakeClientWRestMapper()
fakeClient, _ := test.NewFakeClientWithProm()
testLogger := testlogr.NewTestLogger(t)
// Resources to create
cfg := telemetry.PrometheusResourcer{
Expand Down
18 changes: 11 additions & 7 deletions controllers/k8ssandra/stop_dc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,21 @@ func stopExistingDc(t *testing.T, f *framework.Framework, ctx context.Context, k

replication := map[string]int{"dc1": 3, "dc2": 3}
stopDcManagementApiReset(replication)

t.Log("stop dc1")
patch := client.MergeFromWithOptions(kc.DeepCopy(), client.MergeFromWithOptimisticLock{})
kc.Spec.Cassandra.Datacenters[0].Stopped = true
err := f.Client.Patch(ctx, kc, patch)
require.NoError(t, err, "failed to patch kc")
require.Eventually(t, func() bool {
kc = &api.K8ssandraCluster{}
f.Client.Get(ctx, kcKey, kc)
patch := client.MergeFromWithOptions(kc.DeepCopy(), client.MergeFromWithOptimisticLock{})
kc.Spec.Cassandra.Datacenters[0].Stopped = true
err := f.Client.Patch(ctx, kc, patch)
t.Log(err)
return err == nil
}, timeout, interval, "failed to patch dc1 status to stopped")
withDc1 := f.NewWithDatacenter(ctx, dc1Key)
require.Eventually(t, withDc1(func(dc1 *cassdcapi.CassandraDatacenter) bool {
return assert.True(t, dc1.Spec.Stopped)
}), timeout, interval, "timeout waiting for dc1 to be stopped")
err = f.SetDatacenterStatusStopped(ctx, dc1Key)
err := f.SetDatacenterStatusStopped(ctx, dc1Key)
require.NoError(t, err, "failed to set dc1 status stopped")

t.Log("wait for the dc conditions to be met")
Expand All @@ -211,7 +215,7 @@ func stopExistingDc(t *testing.T, f *framework.Framework, ctx context.Context, k
require.Eventually(t, f.ReaperExists(ctx, reaper2Key), timeout, interval, "failed to verify reaper reaper2 created")

t.Log("start dc1")
patch = client.MergeFromWithOptions(kc.DeepCopy(), client.MergeFromWithOptimisticLock{})
patch := client.MergeFromWithOptions(kc.DeepCopy(), client.MergeFromWithOptimisticLock{})
kc.Spec.Cassandra.Datacenters[0].Stopped = false
err = f.Client.Patch(ctx, kc, patch)
require.NoError(t, err, "failed to patch kc")
Expand Down
5 changes: 3 additions & 2 deletions controllers/stargate/stargate_telemetry_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package stargate
import (
"context"
"errors"
"github.com/k8ssandra/k8ssandra-operator/pkg/validation"

k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1"

Expand Down Expand Up @@ -35,11 +36,11 @@ func (r *StargateReconciler) reconcileStargateTelemetry(
cfg.CommonLabels[k8ssandraapi.K8ssandraClusterNameLabel] = klusterName
}
// Confirm telemetry config is valid (e.g. Prometheus is installed if it is requested.)
promInstalled, err := telemetry.IsPromInstalled(remoteClient, logger)
promInstalled, err := validation.IsPromInstalled(remoteClient, logger)
Miles-Garnsey marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ctrl.Result{}, err
}
validConfig, err := telemetry.SpecIsValid(thisStargate.Spec.Telemetry, promInstalled)
validConfig, err := validation.TelemetrySpecIsValid(thisStargate.Spec.Telemetry, promInstalled)
if err != nil {
return ctrl.Result{}, errors.New("could not determine if telemetry config is valid")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func Test_reconcileStargateTelemetry_succeeds(t *testing.T) {
// Test fixtures
r := newDummyK8ssandraClusterReconciler()
ctx := context.Background()
fakeClient := test.NewFakeClientWRestMapper()
fakeClient, _ := test.NewFakeClientWithProm()
testLogger := testlogr.NewTestLogger(t)
// Resources to create
stargate := test.NewStargate("test-stargate", "test-stargate-namespace")
Expand Down
2 changes: 1 addition & 1 deletion pkg/encryption/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/images/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions pkg/telemetry/prometheus_resourcer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ import (

// Test_PrometheusResourcer_UpdateResources_Create_CassDC tests that a serviceMonitor is created if one does not exist.
func Test_PrometheusResourcer_UpdateResources_Create_CassDC(t *testing.T) {
fakeClient, err := test.NewFakeClient()
if err != nil {
assert.Fail(t, "could not create fake client", err)
}
fakeClient, _ := test.NewFakeClientWithProm()
ctx := context.Background()
// Create k8ssandra cluster and pass through to PrometheusResourcer.UpdateResources()
logger := testlogr.NewTestLogger(t)
Expand Down Expand Up @@ -54,7 +51,7 @@ func Test_PrometheusResourcer_UpdateResources_Create_CassDC(t *testing.T) {
// Test_PrometheusResourcer_Cleanup_CassDC tests that the Cleanup method on PrometheusResourcer correctly
// cleans up all resources it creates using its UpdateResources method.
func Test_PrometheusResourcer_Cleanup_CassDC(t *testing.T) {
fakeClient, err := test.NewFakeClient()
fakeClient, err := test.NewFakeClientWithProm()
if err != nil {
assert.Fail(t, "could not create fake client", err)
}
Expand Down
Loading