diff: remove `Differ` code

In favor of the new `Diff` action.

Signed-off-by: Hidde Beydals <hidde@hhh.computer>
This commit is contained in:
Hidde Beydals 2023-10-27 23:00:21 +02:00
parent bb6ad72654
commit 6b1effcd2f
No known key found for this signature in database
GPG Key ID: 979F380FC2341744
3 changed files with 1 additions and 397 deletions

2
go.mod
View File

@ -15,7 +15,6 @@ replace (
)
require (
github.com/fluxcd/cli-utils v0.36.0-flux.1
github.com/fluxcd/helm-controller/api v0.36.2
github.com/fluxcd/pkg/apis/acl v0.1.0
github.com/fluxcd/pkg/apis/event v0.6.0
@ -78,6 +77,7 @@ require (
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fluxcd/cli-utils v0.36.0-flux.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect

View File

@ -1,146 +0,0 @@
/*
Copyright 2023 The Flux 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 diff
import (
"context"
"fmt"
"strings"
"helm.sh/helm/v3/pkg/release"
"k8s.io/apimachinery/pkg/util/errors"
ctrl "sigs.k8s.io/controller-runtime"
"github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/ssa"
v2 "github.com/fluxcd/helm-controller/api/v2beta2"
"github.com/fluxcd/helm-controller/internal/util"
)
var (
// MetadataKey is the label or annotation key used to disable the diffing
// of an object.
MetadataKey = v2.GroupVersion.Group + "/driftDetection"
// MetadataDisabledValue is the value used to disable the diffing of an
// object using MetadataKey.
MetadataDisabledValue = "disabled"
)
type Differ struct {
impersonator *client.Impersonator
controllerName string
}
func NewDiffer(impersonator *client.Impersonator, controllerName string) *Differ {
return &Differ{
impersonator: impersonator,
controllerName: controllerName,
}
}
// Manager returns a new ssa.ResourceManager constructed using the client.Impersonator.
func (d *Differ) Manager(ctx context.Context) (*ssa.ResourceManager, error) {
c, poller, err := d.impersonator.GetClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get client to configure resource manager: %w", err)
}
owner := ssa.Owner{
Field: d.controllerName,
}
return ssa.NewResourceManager(c, poller, owner), nil
}
func (d *Differ) Diff(ctx context.Context, rel *release.Release) (*ssa.ChangeSet, bool, error) {
objects, err := ssa.ReadObjects(strings.NewReader(rel.Manifest))
if err != nil {
return nil, false, fmt.Errorf("failed to read objects from release manifest: %w", err)
}
if err := ssa.SetNativeKindsDefaults(objects); err != nil {
return nil, false, fmt.Errorf("failed to set native kind defaults on release objects: %w", err)
}
resourceManager, err := d.Manager(ctx)
if err != nil {
return nil, false, err
}
var (
changeSet = ssa.NewChangeSet()
isNamespacedGVK = map[string]bool{}
diff bool
errs []error
)
for _, obj := range objects {
if obj.GetNamespace() == "" {
// Manifest does not contain the namespace of the release.
// Figure out if the object is namespaced if the namespace is not
// explicitly set, and configure the namespace accordingly.
objGVK := obj.GetObjectKind().GroupVersionKind().String()
if _, ok := isNamespacedGVK[objGVK]; !ok {
namespaced, err := util.IsAPINamespaced(obj, resourceManager.Client().Scheme(), resourceManager.Client().RESTMapper())
if err != nil {
errs = append(errs, fmt.Errorf("failed to determine if %s is namespace scoped: %w",
obj.GetObjectKind().GroupVersionKind().Kind, err))
continue
}
// Cache the result, so we don't have to do this for every object
isNamespacedGVK[objGVK] = namespaced
}
if isNamespacedGVK[objGVK] {
obj.SetNamespace(rel.Namespace)
}
}
entry, releaseObject, clusterObject, err := resourceManager.Diff(ctx, obj, ssa.DiffOptions{
Exclusions: map[string]string{
MetadataKey: MetadataDisabledValue,
},
})
if err != nil {
errs = append(errs, err)
}
if entry == nil {
continue
}
switch entry.Action {
case ssa.CreatedAction, ssa.ConfiguredAction:
diff = true
changeSet.Add(*entry)
if entry.Action == ssa.ConfiguredAction {
// TODO: remove this once we have a better way to log the diff
// for example using a custom dyff reporter, or a flux CLI command
if d, equal := Unstructured(releaseObject, clusterObject, WithoutStatus()); !equal {
ctrl.LoggerFrom(ctx).V(logger.DebugLevel).Info(entry.Subject + " diff:\n" + d)
}
}
case ssa.SkippedAction:
changeSet.Add(*entry)
}
}
err = errors.Reduce(errors.Flatten(errors.NewAggregate(errs)))
if len(changeSet.Entries) == 0 {
return nil, diff, err
}
return changeSet, diff, err
}

View File

@ -1,250 +0,0 @@
/*
Copyright 2023 The Flux 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 diff
import (
"context"
"fmt"
"testing"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/fluxcd/cli-utils/pkg/kstatus/polling"
"github.com/fluxcd/cli-utils/pkg/object"
runtimeClient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/ssa"
"helm.sh/helm/v3/pkg/release"
)
func TestDiffer_Diff(t *testing.T) {
scheme, mapper := testSchemeWithMapper()
// We do not test all the possible scenarios here, as the ssa package is
// already tested in depth. We only test the integration with the ssa package.
tests := []struct {
name string
client client.Client
rel *release.Release
want *ssa.ChangeSet
wantDrift bool
wantErr string
}{
{
name: "manifest read error",
client: fake.NewClientBuilder().Build(),
rel: &release.Release{
Manifest: "invalid",
},
wantErr: "failed to read objects from release manifest",
},
{
name: "error on failure to determine namespace scope",
client: fake.NewClientBuilder().Build(),
rel: &release.Release{
Namespace: "release",
Manifest: `apiVersion: v1
kind: Secret
metadata:
name: test
stringData:
foo: bar
`,
},
wantErr: "failed to determine if Secret is namespace scoped",
},
{
name: "detects changes",
client: fake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(mapper).
Build(),
rel: &release.Release{
Namespace: "release",
Manifest: `---
apiVersion: v1
kind: Secret
metadata:
name: test
stringData:
foo: bar
---
apiVersion: v1
kind: Secret
metadata:
name: test-ns
namespace: other
stringData:
foo: bar
`,
},
want: &ssa.ChangeSet{
Entries: []ssa.ChangeSetEntry{
{
ObjMetadata: object.ObjMetadata{
Namespace: "release",
Name: "test",
GroupKind: schema.GroupKind{
Kind: "Secret",
},
},
GroupVersion: "v1",
Subject: "Secret/release/test",
Action: ssa.CreatedAction,
},
{
ObjMetadata: object.ObjMetadata{
Namespace: "other",
Name: "test-ns",
GroupKind: schema.GroupKind{
Kind: "Secret",
},
},
GroupVersion: "v1",
Subject: "Secret/other/test-ns",
Action: ssa.CreatedAction,
},
},
},
wantDrift: true,
},
{
name: "ignores exclusions",
client: fake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(mapper).
Build(),
rel: &release.Release{
Namespace: "release",
Manifest: fmt.Sprintf(`---
apiVersion: v1
kind: Secret
metadata:
name: test
labels:
%[1]s: %[2]s
stringData:
foo: bar
---
apiVersion: v1
kind: Secret
metadata:
name: test2
stringData:
foo: bar
`, MetadataKey, MetadataDisabledValue),
},
want: &ssa.ChangeSet{
Entries: []ssa.ChangeSetEntry{
{
ObjMetadata: object.ObjMetadata{
Namespace: "release",
Name: "test",
GroupKind: schema.GroupKind{
Kind: "Secret",
},
},
GroupVersion: "v1",
Subject: "Secret/release/test",
Action: ssa.SkippedAction,
},
{
ObjMetadata: object.ObjMetadata{
Namespace: "release",
Name: "test2",
GroupKind: schema.GroupKind{
Kind: "Secret",
},
},
GroupVersion: "v1",
Subject: "Secret/release/test2",
Action: ssa.CreatedAction,
},
},
},
wantDrift: true,
},
{
name: "ignores exclusions (without diff)",
client: fake.NewClientBuilder().
WithScheme(scheme).
WithRESTMapper(mapper).
Build(),
rel: &release.Release{
Namespace: "release",
Manifest: fmt.Sprintf(`---
apiVersion: v1
kind: Secret
metadata:
name: test
labels:
%[1]s: %[2]s
stringData:
foo: bar`, MetadataKey, MetadataDisabledValue),
},
want: &ssa.ChangeSet{
Entries: []ssa.ChangeSetEntry{
{
ObjMetadata: object.ObjMetadata{
Namespace: "release",
Name: "test",
GroupKind: schema.GroupKind{
Kind: "Secret",
},
},
GroupVersion: "v1",
Subject: "Secret/release/test",
Action: ssa.SkippedAction,
},
},
},
wantDrift: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
d := NewDiffer(runtimeClient.NewImpersonator(tt.client, nil, polling.Options{}, nil, runtimeClient.KubeConfigOptions{}, "", "", ""), "test-controller")
got, drift, err := d.Diff(context.TODO(), tt.rel)
if tt.wantErr != "" {
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring(tt.wantErr))
} else {
g.Expect(err).NotTo(HaveOccurred())
}
g.Expect(got).To(Equal(tt.want))
g.Expect(drift).To(Equal(tt.wantDrift))
})
}
}
func testSchemeWithMapper() (*runtime.Scheme, meta.RESTMapper) {
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
mapper.Add(corev1.SchemeGroupVersion.WithKind("Secret"), meta.RESTScopeNamespace)
return scheme, mapper
}