Skip to content

Commit

Permalink
update prune to accept of slice of unstrucured
Browse files Browse the repository at this point in the history
  • Loading branch information
Liujingfang1 committed Jun 11, 2019
1 parent 87d28f5 commit ddcdd2f
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 58 deletions.
2 changes: 1 addition & 1 deletion internal/pkg/clik8s/clik8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ type MasterURL string
type ResourceConfigs []*unstructured.Unstructured

// ResourcePruneConfigs is a collection of Resource Config used for pruning
type ResourcePruneConfigs *unstructured.Unstructured
type ResourcePruneConfigs []*unstructured.Unstructured
73 changes: 62 additions & 11 deletions internal/pkg/prune/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ import (
"fmt"
"io"
"os"
"sigs.k8s.io/cli-experimental/internal/pkg/util"

"k8s.io/apimachinery/pkg/types"

"gopkg.in/src-d/go-git.v4/plumbing/object"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/cli-experimental/internal/pkg/client"
"sigs.k8s.io/cli-experimental/internal/pkg/clik8s"
"sigs.k8s.io/cli-experimental/internal/pkg/constants"
"sigs.k8s.io/cli-experimental/internal/pkg/util"
"sigs.k8s.io/kustomize/pkg/inventory"
)

Expand Down Expand Up @@ -62,15 +62,24 @@ func (o *Prune) Do() (Result, error) {
fmt.Fprintf(o.Out, "Doing `cli-experimental prune`\n")
ctx := context.Background()

u := (*unstructured.Unstructured)(o.Resources)
annotation := u.GetAnnotations()
_, ok := annotation[inventory.InventoryAnnotation]
if !ok {
u, found, err := o.findInventoryObject()
if err != nil {
return Result{}, nil
}

// Couldn't find an inventory object
// Will handle the pruning from
// annotation kubectl.kubernetes.io/presence: EnsureDoesNotExist
if !found {
result, err := o.runPruneWithAnnotation(ctx)
if err != nil {
return Result{}, err
}
return Result{result}, nil
}

obj := u.DeepCopy()
err := o.DynamicClient.Get(ctx,
err = o.DynamicClient.Get(ctx,
types.NamespacedName{Namespace: u.GetNamespace(), Name: u.GetName()}, obj)

if err != nil {
Expand All @@ -81,7 +90,7 @@ func (o *Prune) Do() (Result, error) {
fmt.Fprintf(os.Stderr, "retrieving current configuration of %s from server for %v", u.GetName(), err)
return Result{}, err
}
obj, results, err := o.runPrune(ctx, obj)
obj, results, err := o.runPruneWithInventory(ctx, obj)
if err != nil {
return Result{}, err
}
Expand All @@ -94,14 +103,14 @@ func (o *Prune) Do() (Result, error) {
return Result{Resources: results}, nil
}

// runPrune deletes the obsolete objects.
// runPruneWithInventory deletes the obsolete objects.
// The obsolete objects is derived by parsing
// an Inventory annotation, which is defined in
// Kustomize.
// https://github.com/kubernetes-sigs/kustomize/tree/master/pkg/inventory
// This is based on the KEP
// https://github.com/kubernetes/enhancements/pull/810
func (o *Prune) runPrune(ctx context.Context, obj *unstructured.Unstructured) (
func (o *Prune) runPruneWithInventory(ctx context.Context, obj *unstructured.Unstructured) (
*unstructured.Unstructured, []*unstructured.Unstructured, error) {
var results []*unstructured.Unstructured
annotations := obj.GetAnnotations()
Expand All @@ -127,3 +136,45 @@ func (o *Prune) runPrune(ctx context.Context, obj *unstructured.Unstructured) (
return obj, results, nil
}

// runPruneWithAnnotation deletes the objects
// that are with the annotation
// kubectl.kubernetes.io/presence: EnsureDoesNotExist
func (o *Prune) runPruneWithAnnotation(ctx context.Context) (
[]*unstructured.Unstructured, error) {
var results []*unstructured.Unstructured
for _, r := range o.Resources {
annotation := r.GetAnnotations()
presence, ok := annotation[constants.Presence]
if ok && presence == constants.EnsureNoExist {
u, err := util.DeleteObject(o.DynamicClient, ctx, r.GroupVersionKind(), r.GetNamespace(), r.GetName())
if err != nil {
return nil, err
}
if u != nil {
results = append(results, u)
}
}
}
return results, nil
}

// findInventoryObject find if there is an inventory object in
// the resources
func (o *Prune) findInventoryObject() (*unstructured.Unstructured, bool, error) {
var u *unstructured.Unstructured
found := false
for _, r := range o.Resources {
annotation := r.GetAnnotations()
_, ok := annotation[inventory.InventoryAnnotation]
if ok {
if !found {
found = true
u = r
} else {
return nil, false,
fmt.Errorf("multiple objects with the inventory annotation")
}
}
}
return u, found, nil
}
207 changes: 202 additions & 5 deletions internal/pkg/prune/prune_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestPruneWithoutInventory(t *testing.T) {
assert.Equal(t, len(fs), 2)

// run the prune
pruneObject, err := kp.GetPruneConfig(fs[1])
pruneObject, err := kp.GetConfig(fs[1])
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
Expand Down Expand Up @@ -109,7 +109,7 @@ func TestPruneOneObject(t *testing.T) {
assert.Equal(t, len(cmList.Items), 3)

// run the prune
pruneObject, err := kp.GetPruneConfig(fs[1])
pruneObject, err := kp.GetConfig(fs[1])
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
Expand Down Expand Up @@ -248,7 +248,7 @@ secretGenerator:
assert.Equal(t, len(sList.Items), 2)

// Run prune and assert there are no objects get deleted
pruneObject, err := kp.GetPruneConfig(f2)
pruneObject, err := kp.GetConfig(f2)
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
Expand Down Expand Up @@ -388,7 +388,7 @@ secretGenerator:
assert.Equal(t, len(sList.Items), 2)

// Run prune and assert there are no objects get deleted
pruneObject, err := kp.GetPruneConfig(f2)
pruneObject, err := kp.GetConfig(f2)
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
Expand Down Expand Up @@ -582,7 +582,7 @@ namePrefix: test-
assert.Equal(t, len(svList.Items), serviceNumber+2)

// Run prune and assert there are 3 objects get deleted
pruneObject, err := kp.GetPruneConfig(f2)
pruneObject, err := kp.GetConfig(f2)
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
Expand All @@ -607,3 +607,200 @@ namePrefix: test-
assert.NoError(t, err)
assert.Equal(t, len(svList.Items), serviceNumber+1)
}

func setupKustomizeWithDeclarativeDeletion(s string) (string, error) {
f, err := ioutil.TempDir("/tmp", "TestApply")
if err != nil {
return "", err
}
err = ioutil.WriteFile(filepath.Join(f, "kustomization.yaml"), []byte(`apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
secretGenerator:
- name: pass
literals:
- password=secret
namespace: default
`), 0644)
if err != nil {
return "", err
}

err = ioutil.WriteFile(filepath.Join(f, "deployment.yaml"), []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: pass
key: password
`), 0644)
if err != nil {
return "", err
}

err = ioutil.WriteFile(filepath.Join(f, "service.yaml"), []byte(`
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
` + s + `
spec:
ports:
- port: 3306
selector:
app: mysql
`), 0644)
if err != nil {
return "", err
}

return f, nil
}

/* TestPruneWithAnnotations take following steps
1. create a Kustomization with
a SecretGenerator
a Deployment that uses the generated Secret
a Service
2. apply the kustomization
3. confirm that there are
1 Secret
1 Deployment
1 Service
4. add the following annotation to the Service resource
kubectl.kubernetes.io/presence: EnsureDoesNotExist
4. apply the kustomization again
6. run prune and confirms 1 objects is deleted
7. confirm that there are
1 Secret
1 Deployment
0 Service
*/
func TestPruneWithAnnotations(t *testing.T) {
buf := new(bytes.Buffer)
kp := wiretest.InitializConfigProvider()
ctx := context.Background()

// setup the first version of resource configurations
// and run apply
f1, err := setupKustomizeWithDeclarativeDeletion("")
assert.NoError(t, err)
defer os.RemoveAll(f1)
assert.NoError(t, err)
objects, err := kp.GetConfig(f1)
assert.NoError(t, err)
a, donea, err := wiretest.InitializeApply(objects, &object.Commit{}, buf)
assert.NoError(t, err)
defer donea()

svList := &unstructured.UnstructuredList{}
svList.SetGroupVersionKind(schema.GroupVersionKind{
Kind: "ServiceList",
Version: "v1",
})
err = a.DynamicClient.List(ctx, svList, "default", nil)
assert.NoError(t, err)
serviceNumber := len(svList.Items)

_, err = a.Do()
assert.NoError(t, err)

// Confirm that there is one Service
err = a.DynamicClient.List(ctx, svList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(svList.Items), serviceNumber+1)

// Confirm that there is one Secret
sList := &unstructured.UnstructuredList{}
sList.SetGroupVersionKind(schema.GroupVersionKind{
Kind: "SecretList",
Version: "v1",
})
err = a.DynamicClient.List(ctx, sList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(sList.Items), 1)

// Confirm that there is one Deployment
dpList := &unstructured.UnstructuredList{}
dpList.SetGroupVersionKind(schema.GroupVersionKind{
Kind: "DeploymentList",
Version: "v1",
Group: "apps",
})
err = a.DynamicClient.List(ctx, dpList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(dpList.Items), 1)


// setup the second version of resource configurations
// and run apply
f2, err := setupKustomizeWithDeclarativeDeletion(`
annotations:
kubectl.kubernetes.io/presence: EnsureDoesNotExist
`)
assert.NoError(t, err)
defer os.RemoveAll(f2)
a.Resources, err = kp.GetConfig(f2)
assert.NoError(t, err)
_, err = a.Do()
assert.NoError(t, err)

// Confirm that there is one Service
err = a.DynamicClient.List(ctx, svList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(svList.Items), serviceNumber+1)

// Run prune and assert there is one object deleted
pruneObject, err := kp.GetConfig(f2)
assert.NoError(t, err)
p, donep, err := wiretest.InitializePrune(pruneObject, &object.Commit{}, buf)
defer donep()
assert.NoError(t, err)
p.DynamicClient = a.DynamicClient
pr, err := p.Do()
assert.NoError(t, err)
assert.Equal(t, len(pr.Resources), 1)

// Confirm that there are one Secret
err = a.DynamicClient.List(ctx, sList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(sList.Items), 1)

// Confirm that there are one Deployment
err = a.DynamicClient.List(ctx, dpList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(dpList.Items), 1)

// Confirm that there is no Service
err = a.DynamicClient.List(ctx, svList, "default", nil)
assert.NoError(t, err)
assert.Equal(t, len(svList.Items), serviceNumber)
}
Loading

0 comments on commit ddcdd2f

Please sign in to comment.