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

use preprocessing to handle the first time apply with inventorypolicy #1382

Merged
merged 3 commits into from
Feb 1, 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
16 changes: 12 additions & 4 deletions commands/applycmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import (

"github.com/GoogleContainerTools/kpt/internal/util/setters"
"github.com/GoogleContainerTools/kpt/pkg/live"
"github.com/GoogleContainerTools/kpt/pkg/live/preprocess"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/cli-utils/cmd/apply"
"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"
)
Expand All @@ -24,7 +27,7 @@ func GetApplyRunner(provider provider.Provider, loader manifestreader.ManifestLo
applyRunner := apply.GetApplyRunner(provider, loader, ioStreams)
w := &ApplyRunnerWrapper{
applyRunner: applyRunner,
factory: provider.Factory(),
provider: provider,
}
// Set the wrapper run to be the RunE function for the wrapped command.
applyRunner.Command.RunE = w.RunE
Expand All @@ -36,7 +39,7 @@ func GetApplyRunner(provider provider.Provider, loader manifestreader.ManifestLo
// as structures necessary to run.
type ApplyRunnerWrapper struct {
applyRunner *apply.ApplyRunner
factory cmdutil.Factory
provider provider.Provider
}

// Command returns the wrapped ApplyRunner cobraCommand structure.
Expand All @@ -60,11 +63,16 @@ func (w *ApplyRunnerWrapper) PreRunE(_ *cobra.Command, args []string) error {
func (w *ApplyRunnerWrapper) RunE(cmd *cobra.Command, args []string) error {
if _, exists := os.LookupEnv(resourceGroupEnv); exists {
klog.V(4).Infoln("wrapper applyRunner detected environment variable")
err := live.ApplyResourceGroupCRD(w.factory)
err := live.ApplyResourceGroupCRD(w.provider.Factory())
if err != nil && !apierrors.IsAlreadyExists(err) {
return err
}
}
klog.V(4).Infoln("wrapper applyRunner run...")
if w.Command().Flag(flagutils.InventoryPolicyFlag).Value.String() == flagutils.InventoryPolicyStrict {
w.applyRunner.PreProcess = func(inv inventory.InventoryInfo, strategy common.DryRunStrategy) (inventory.InventoryPolicy, error) {
return preprocess.PreProcess(w.provider, inv, strategy)
}
}
return w.applyRunner.RunE(cmd, args)
}
51 changes: 51 additions & 0 deletions commands/destroycmd.go
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)
}
3 changes: 1 addition & 2 deletions commands/livecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog"
"k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/cli-utils/cmd/destroy"
"sigs.k8s.io/cli-utils/cmd/diff"
"sigs.k8s.io/cli-utils/cmd/initcmd"
"sigs.k8s.io/cli-utils/cmd/status"
Expand Down Expand Up @@ -99,7 +98,7 @@ func GetLiveCommand(name string, f util.Factory) *cobra.Command {
diffCmd.Long = livedocs.DiffShort + "\n" + livedocs.DiffLong
diffCmd.Example = livedocs.DiffExamples

destroyCmd := destroy.GetDestroyRunner(p, l, ioStreams).Command
destroyCmd := GetDestroyRunner(p, l, ioStreams).Command()
destroyCmd.Short = livedocs.DestroyShort
destroyCmd.Long = livedocs.DestroyShort + "\n" + livedocs.DestroyLong
destroyCmd.Example = livedocs.DestroyExamples
Expand Down
14 changes: 11 additions & 3 deletions commands/previewcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ package commands

import (
"github.com/GoogleContainerTools/kpt/internal/util/setters"
"github.com/GoogleContainerTools/kpt/pkg/live/preprocess"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/cli-utils/cmd/flagutils"
"sigs.k8s.io/cli-utils/cmd/preview"
"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"
)
Expand All @@ -30,7 +33,7 @@ func GetPreviewRunner(provider provider.Provider, loader manifestreader.Manifest
previewRunner := preview.GetPreviewRunner(provider, loader, ioStreams)
w := &PreviewRunnerWrapper{
previewRunner: previewRunner,
factory: provider.Factory(),
provider: provider,
}
// Set the wrapper run to be the RunE function for the wrapped command.
previewRunner.Command.RunE = w.RunE
Expand All @@ -42,7 +45,7 @@ func GetPreviewRunner(provider provider.Provider, loader manifestreader.Manifest
// as structures necessary to run.
type PreviewRunnerWrapper struct {
previewRunner *preview.PreviewRunner
factory cmdutil.Factory
provider provider.Provider
}

// Command returns the wrapped PreviewRunner cobraCommand structure.
Expand All @@ -63,5 +66,10 @@ func (w *PreviewRunnerWrapper) PreRunE(_ *cobra.Command, args []string) error {
// exists in the package path. Then the wrapped PreviewRunner is
// invoked. Returns an error if one happened.
func (w *PreviewRunnerWrapper) RunE(cmd *cobra.Command, args []string) error {
if w.Command().Flag(flagutils.InventoryPolicyFlag).Value.String() == flagutils.InventoryPolicyStrict {
w.previewRunner.PreProcess = func(inv inventory.InventoryInfo, strategy common.DryRunStrategy) (inventory.InventoryPolicy, error) {
return preprocess.PreProcess(w.provider, inv, strategy)
}
}
return w.previewRunner.RunE(cmd, args)
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
k8s.io/client-go v0.18.10
k8s.io/klog v1.0.0
k8s.io/kubectl v0.18.10
sigs.k8s.io/cli-utils v0.22.4-0.20210108175429-beb6f88a4384
sigs.k8s.io/cli-utils v0.22.5-0.20210127192708-27cfaa675296
sigs.k8s.io/kustomize/cmd/config v0.8.7-0.20201211170716-cc43a2d732d1
sigs.k8s.io/kustomize/kyaml v0.10.5
sigs.k8s.io/kustomize/kyaml v0.10.6
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,8 @@ k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTU
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/cli-utils v0.22.4-0.20210108175429-beb6f88a4384 h1:JepLxW87TlpfpA2i7KiN86ei1cS6dbCLAebkSxz7FNw=
sigs.k8s.io/cli-utils v0.22.4-0.20210108175429-beb6f88a4384/go.mod h1:iWbsn/CCh80/SeoecTqkAso1a+ifiHQX0x5PlZ6zjD8=
sigs.k8s.io/cli-utils v0.22.5-0.20210127192708-27cfaa675296 h1:GcA9Uu6GfyLw7X+djhGV2h0bbcouZdVy0NIvlv7LTaU=
sigs.k8s.io/cli-utils v0.22.5-0.20210127192708-27cfaa675296/go.mod h1:J32lRfzTB7eeQQGnXQQThd5BlhmYZuYuAAdFTQ4oSNc=
sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
Expand All @@ -695,8 +695,8 @@ sigs.k8s.io/kustomize/cmd/config v0.8.7-0.20201211170716-cc43a2d732d1 h1:hn0F38X
sigs.k8s.io/kustomize/cmd/config v0.8.7-0.20201211170716-cc43a2d732d1/go.mod h1:e4PgdLUNnkf+Iapvjyb6gTG9DZQkDZIR6uS1Bv4YA6s=
sigs.k8s.io/kustomize/kyaml v0.10.3 h1:ARSJUMN/c3k31DYxRfZ+vp/UepUQjg9zCwny7Oj908I=
sigs.k8s.io/kustomize/kyaml v0.10.3/go.mod h1:RA+iCHA2wPCOfv6uG6TfXXWhYsHpgErq/AljxWKuxtg=
sigs.k8s.io/kustomize/kyaml v0.10.5 h1:PbJcsZsEM7O3hHtUWTR+4WkHVbQRW9crSy75or1gRbI=
sigs.k8s.io/kustomize/kyaml v0.10.5/go.mod h1:P6Oy/ah/GZMKzJMIJA2a3/bc8YrBkuL5kJji13PSIzY=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
Expand Down
48 changes: 48 additions & 0 deletions pkg/live/preprocess/process.go
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
}
131 changes: 131 additions & 0 deletions pkg/live/preprocess/process_test.go
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
}