Skip to content

Commit

Permalink
add recreate option for update-policy directive
Browse files Browse the repository at this point in the history
  • Loading branch information
kvaps committed Dec 21, 2020
1 parent 3ef9f8e commit 22b404c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 13 deletions.
1 change: 1 addition & 0 deletions internal/commands/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func doApply(args []string, config applyCommandConfig) error {

opts := config.syncOptions
opts.DisableUpdateFn = newUpdatePolicy().disableUpdate
opts.RecreateUpdateFn = newUpdatePolicy().recreateUpdate

if !opts.DryRun && len(objects) > 0 {
msg := fmt.Sprintf("will synchronize %d object(s)", len(objects))
Expand Down
9 changes: 7 additions & 2 deletions internal/commands/directives.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import (
)

const (
policyNever = "never"
policyDefault = "default"
policyNever = "never"
policyRecreate = "recreate"
policyDefault = "default"
)

// isSet return true if the annotation name specified as directive is equal to the supplied value.
Expand Down Expand Up @@ -64,6 +65,10 @@ func (u *updatePolicy) disableUpdate(ob model.K8sMeta) bool {
return isSet(ob, model.QbecNames.Directives.UpdatePolicy, policyNever, []string{policyDefault})
}

func (u *updatePolicy) recreateUpdate(ob model.K8sMeta) bool {
return isSet(ob, model.QbecNames.Directives.UpdatePolicy, policyRecreate, []string{policyDefault})
}

func newUpdatePolicy() *updatePolicy {
return &updatePolicy{}
}
Expand Down
13 changes: 12 additions & 1 deletion internal/commands/directives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func TestDirectivesIsSet(t *testing.T) {
}
}

func TestDirectivesUpdatePolicy(t *testing.T) {
func TestDirectivesUpdatePolicyNever(t *testing.T) {
up := newUpdatePolicy()
a := assert.New(t)
ret := up.disableUpdate(k8sMetaWithAnnotations("ConfigMap", "foo", "bar", nil))
Expand All @@ -92,6 +92,17 @@ func TestDirectivesUpdatePolicy(t *testing.T) {
a.True(ret)
}

func TestDirectivesUpdatePolicyRecreate(t *testing.T) {
up := newUpdatePolicy()
a := assert.New(t)
ret := up.recreateUpdate(k8sMetaWithAnnotations("ConfigMap", "foo", "bar", nil))
a.False(ret)
ret = up.recreateUpdate(k8sMetaWithAnnotations("ConfigMap", "foo", "bar", map[string]interface{}{
"directives.qbec.io/update-policy": "recreate",
}))
a.True(ret)
}

func TestDirectivesDeletePolicy(t *testing.T) {
dp := newDeletePolicy(func(gvk schema.GroupVersionKind) (bool, error) {
return gvk.Kind == "ConfigMap", nil
Expand Down
55 changes: 46 additions & 9 deletions internal/remote/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
apiTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
)
Expand Down Expand Up @@ -64,11 +65,12 @@ type ConditionFunc func(obj model.K8sMeta) bool

// SyncOptions provides the caller with options for the sync operation.
type SyncOptions struct {
DryRun bool // do not actually create or update objects, return what would happen
DisableCreate bool // only update objects if they exist, do not create new ones
DisableUpdateFn ConditionFunc // do not update an existing object
WaitOptions TypeWaitOptions // opts for waiting
ShowSecrets bool // show secrets in patches and creations
DryRun bool // do not actually create or update objects, return what would happen
DisableCreate bool // only update objects if they exist, do not create new ones
DisableUpdateFn ConditionFunc // do not update an existing object
RecreateUpdateFn ConditionFunc // recreate existing object on update
WaitOptions TypeWaitOptions // opts for waiting
ShowSecrets bool // show secrets in patches and creations
}

// DeleteOptions provides the caller with options for the delete operation.
Expand Down Expand Up @@ -647,6 +649,35 @@ func (c *Client) maybeCreate(obj model.K8sLocalObject, opts SyncOptions) (*updat
return result, nil
}

func (c *Client) doRecreate(obj model.K8sLocalObject, opts SyncOptions) (*updateResult, error) {
ri, err := c.resourceInterfaceWithDefaultNs(obj.GroupVersionKind(), obj.GetNamespace())
if err != nil {
return nil, errors.Wrap(err, "get resource interface")
}

sio.Debugln("delete " + c.DisplayName(obj))
pp := metav1.DeletePropagationForeground
err = ri.Delete(obj.GetName(), &metav1.DeleteOptions{PropagationPolicy: &pp})
if err != nil && !apiErrors.IsNotFound(err) {
return nil, err
}

watcher, err := ri.Watch(metav1.ListOptions{})
if err != nil {
return nil, err
}

sio.Debugln("wait " + c.DisplayName(obj))
for {
ev := <-watcher.ResultChan()
if ev.Type == watch.Deleted {
break
}
}
watcher.Stop()
return c.maybeCreate(obj, opts)
}

func (c *Client) maybeUpdate(obj model.K8sLocalObject, remObj *unstructured.Unstructured, opts SyncOptions) (*updateResult, error) {
if opts.DisableUpdateFn(model.NewK8sObject(remObj.Object)) {
return &updateResult{
Expand Down Expand Up @@ -686,10 +717,16 @@ func (c *Client) maybeUpdate(obj model.K8sLocalObject, remObj *unstructured.Unst
}

var result *updateResult
if opts.DryRun {
result, err = p.getPatchContents(remObj, obj)
} else {
result, err = p.patch(remObj, obj)

patch, err := p.getPatchContents(remObj, obj)
if err != nil || opts.DryRun {
return patch, err
}
if patch.SkipReason != identicalObjects && opts.RecreateUpdateFn(model.NewK8sObject(remObj.Object)) {
return c.doRecreate(obj, opts)
}

result, err = p.patch(remObj, obj)

return result, err
}
3 changes: 2 additions & 1 deletion site/content/reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ object to remove this annotation will not work.
#### `directives.qbec.io/update-policy`

* Annotation source: in-cluster object.
* Allowed values: `"default"`, `"never"`
* Allowed values: `"default"`, `"never"`, `"recreate"`
* Default value: `"default"`

when set to `"never"`, indicates that the specific object should never be updated.
when set to `"recreate"`, indicates that the specific object should be recreated instad of updating.
If you want qbec to update this object, you need to remove the annotation from the in-cluster object. Changing the source
object to remove this annotation will not work.

Expand Down

0 comments on commit 22b404c

Please sign in to comment.