-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use preprocessing to handle the first time apply with inventorypolicy (…
…#1382) * use preprocess function to handle the first time apply with inventory policy * update dependency * add unit test for pre processing function
- Loading branch information
1 parent
8a1032f
commit 8695bbb
Showing
8 changed files
with
260 additions
and
15 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package commands | ||
|
||
import ( | ||
"github.com/GoogleContainerTools/kpt/pkg/live/preprocess" | ||
"github.com/spf13/cobra" | ||
"k8s.io/cli-runtime/pkg/genericclioptions" | ||
"sigs.k8s.io/cli-utils/cmd/destroy" | ||
"sigs.k8s.io/cli-utils/cmd/flagutils" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
"sigs.k8s.io/cli-utils/pkg/manifestreader" | ||
"sigs.k8s.io/cli-utils/pkg/provider" | ||
) | ||
|
||
// GetDestroyRunner returns a wrapper around the cli-utils destroy command DestroyRunner. Sets | ||
// up the Run on this wrapped runner to be the DestroyRunnerWrapper run. | ||
func GetDestroyRunner(provider provider.Provider, loader manifestreader.ManifestLoader, ioStreams genericclioptions.IOStreams) *DestroyRunnerWrapper { | ||
destroyRunner := destroy.GetDestroyRunner(provider, loader, ioStreams) | ||
w := &DestroyRunnerWrapper{ | ||
destroyRunner: destroyRunner, | ||
provider: provider, | ||
} | ||
// Set the wrapper run to be the RunE function for the wrapped command. | ||
destroyRunner.Command.RunE = w.RunE | ||
return w | ||
} | ||
|
||
// DestroyRunnerWrapper encapsulates the cli-utils destroy command DestroyRunner as well | ||
// as structures necessary to run. | ||
type DestroyRunnerWrapper struct { | ||
destroyRunner *destroy.DestroyRunner | ||
provider provider.Provider | ||
} | ||
|
||
// Command returns the wrapped DestroyRunner cobraCommand structure. | ||
func (w *DestroyRunnerWrapper) Command() *cobra.Command { | ||
return w.destroyRunner.Command | ||
} | ||
|
||
// RunE wraps the destroyRunner.RunE with the pre-processing for inventory policy. | ||
func (w *DestroyRunnerWrapper) RunE(cmd *cobra.Command, args []string) error { | ||
if w.Command().Flag(flagutils.InventoryPolicyFlag).Value.String() == flagutils.InventoryPolicyStrict { | ||
w.destroyRunner.PreProcess = func(inv inventory.InventoryInfo, strategy common.DryRunStrategy) (inventory.InventoryPolicy, error) { | ||
return preprocess.PreProcess(w.provider, inv, strategy) | ||
} | ||
} | ||
return w.destroyRunner.RunE(cmd, args) | ||
} |
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,48 @@ | ||
// Copyright 2021 Google LLC. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package preprocess | ||
|
||
import ( | ||
"fmt" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
"sigs.k8s.io/cli-utils/pkg/provider" | ||
) | ||
|
||
func PreProcess(p provider.Provider, inv inventory.InventoryInfo, strategy common.DryRunStrategy) (inventory.InventoryPolicy, error) { | ||
invClient, err := p.InventoryClient() | ||
if err != nil { | ||
return inventory.InventoryPolicyMustMatch, err | ||
} | ||
obj, err := invClient.GetClusterInventoryInfo(inv) | ||
if err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return inventory.InventoryPolicyMustMatch, nil | ||
} | ||
return inventory.InventoryPolicyMustMatch, err | ||
} | ||
|
||
if obj == nil { | ||
return inventory.InventoryPolicyMustMatch, nil | ||
} | ||
|
||
managedByKey := "apps.kubernetes.io/managed-by" | ||
managedByVal := "kpt" | ||
labels := obj.GetLabels() | ||
val, found := labels[managedByKey] | ||
if found { | ||
if val != managedByVal { | ||
return inventory.InventoryPolicyMustMatch, fmt.Errorf("can't apply the current package since it is managed by %s", val) | ||
} | ||
return inventory.InventoryPolicyMustMatch, nil | ||
} | ||
labels[managedByKey] = managedByVal | ||
if strategy.ClientOrServerDryRun() { | ||
return inventory.AdoptIfNoInventory, nil | ||
} | ||
err = invClient.UpdateLabels(inv, labels) | ||
return inventory.AdoptIfNoInventory, err | ||
} |
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,131 @@ | ||
// Copyright 2021 Google LLC. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package preprocess | ||
|
||
import ( | ||
"testing" | ||
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/kubectl/pkg/cmd/util" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
"sigs.k8s.io/cli-utils/pkg/object" | ||
) | ||
|
||
func TestPreProcess(t *testing.T) { | ||
testcases := []struct { | ||
name string | ||
inventoryObject *unstructured.Unstructured | ||
expected inventory.InventoryPolicy | ||
}{ | ||
{ | ||
name: "nil cluster inventory object", | ||
inventoryObject: nil, | ||
expected: inventory.InventoryPolicyMustMatch, | ||
}, | ||
{ | ||
name: "existing cluster inventory object without managed-by label", | ||
inventoryObject: &unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "v1", | ||
"kind": "ConfigMap", | ||
"metadata": map[string]interface{}{ | ||
"name": "test", | ||
"namespace": "test", | ||
"labels": map[string]interface{}{ | ||
common.InventoryLabel: "test", | ||
}, | ||
}, | ||
}, | ||
}, | ||
expected: inventory.AdoptIfNoInventory, | ||
}, | ||
{ | ||
name: "existing cluster inventory object with managed-by label", | ||
inventoryObject: &unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "v1", | ||
"kind": "ConfigMap", | ||
"metadata": map[string]interface{}{ | ||
"name": "test", | ||
"namespace": "test", | ||
"labels": map[string]interface{}{ | ||
common.InventoryLabel: "test", | ||
"apps.kubernetes.io/managed-by": "kpt", | ||
}, | ||
}, | ||
}, | ||
}, | ||
expected: inventory.InventoryPolicyMustMatch, | ||
}, | ||
} | ||
for _, tc := range testcases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
invClient := &fakeInventoryClient{inventory: tc.inventoryObject} | ||
p := &fakeProvider{invClient: invClient} | ||
actual, err := PreProcess(p, nil, common.DryRunNone) | ||
if err != nil { | ||
t.Fatalf("unexpected error %v", err) | ||
} | ||
if actual != tc.expected { | ||
t.Fatalf("expected %v but got %v", tc.expected, actual) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
type fakeProvider struct { | ||
factory util.Factory | ||
invClient inventory.InventoryClient | ||
} | ||
|
||
func (p *fakeProvider) Factory() util.Factory { | ||
return p.factory | ||
} | ||
|
||
func (p *fakeProvider) InventoryClient() (inventory.InventoryClient, error) { | ||
return p.invClient, nil | ||
} | ||
|
||
type fakeInventoryClient struct { | ||
inventory *unstructured.Unstructured | ||
} | ||
|
||
func (f *fakeInventoryClient) GetClusterObjs(inv inventory.InventoryInfo) ([]object.ObjMetadata, error) { | ||
return nil, nil | ||
} | ||
|
||
func (f *fakeInventoryClient) Merge(inv inventory.InventoryInfo, objs []object.ObjMetadata) ([]object.ObjMetadata, error) { | ||
return nil, nil | ||
} | ||
|
||
// Replace replaces the set of objects stored in the inventory | ||
// object with the passed set of objects, or an error if one occurs. | ||
func (f *fakeInventoryClient) Replace(inv inventory.InventoryInfo, objs []object.ObjMetadata) error { | ||
return nil | ||
} | ||
|
||
// DeleteInventoryObj deletes the passed inventory object from the APIServer. | ||
func (f *fakeInventoryClient) DeleteInventoryObj(inv inventory.InventoryInfo) error { | ||
return nil | ||
} | ||
|
||
// SetDryRunStrategy sets the dry run strategy on whether this we actually mutate. | ||
func (f *fakeInventoryClient) SetDryRunStrategy(drs common.DryRunStrategy) {} | ||
|
||
// ApplyInventoryNamespace applies the Namespace that the inventory object should be in. | ||
func (f *fakeInventoryClient) ApplyInventoryNamespace(invNamespace *unstructured.Unstructured) error { | ||
return nil | ||
} | ||
|
||
// GetClusterInventoryInfo returns the cluster inventory object. | ||
func (f *fakeInventoryClient) GetClusterInventoryInfo(inv inventory.InventoryInfo) (*unstructured.Unstructured, error) { | ||
return f.inventory, nil | ||
} | ||
|
||
// UpdateLabels updates the labels of the cluster inventory object if it exists. | ||
func (f *fakeInventoryClient) UpdateLabels(inv inventory.InventoryInfo, labels map[string]string) error { | ||
f.inventory.SetLabels(labels) | ||
return nil | ||
} |