mirror of https://github.com/fluxcd/cli-utils.git
Improve the event hierarchy
This commit is contained in:
parent
a051c61f67
commit
ae80e561e2
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
|
|
@ -29,78 +27,57 @@ type formatter struct {
|
||||||
print printFunc
|
print printFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats, c list.Collector) error {
|
func (ef *formatter) FormatApplyEvent(ae event.ApplyEvent) error {
|
||||||
switch ae.Type {
|
|
||||||
case event.ApplyEventCompleted:
|
|
||||||
output := fmt.Sprintf("%d resource(s) applied. %d created, %d unchanged, %d configured, %d failed",
|
|
||||||
as.Sum(), as.Created, as.Unchanged, as.Configured, as.Failed)
|
|
||||||
// Only print information about serverside apply if some of the
|
|
||||||
// resources actually were applied serverside.
|
|
||||||
if as.ServersideApplied > 0 {
|
|
||||||
output += fmt.Sprintf(", %d serverside applied", as.ServersideApplied)
|
|
||||||
}
|
|
||||||
ef.print(output)
|
|
||||||
for id, se := range c.LatestStatus() {
|
|
||||||
ef.printResourceStatus(id, se)
|
|
||||||
}
|
|
||||||
case event.ApplyEventResourceUpdate:
|
|
||||||
gk := ae.Identifier.GroupKind
|
gk := ae.Identifier.GroupKind
|
||||||
name := ae.Identifier.Name
|
name := ae.Identifier.Name
|
||||||
if ae.Error != nil {
|
if ae.Error != nil {
|
||||||
ef.print("%s failed: %s", resourceIDToString(gk, name),
|
ef.print("%s apply failed: %s", resourceIDToString(gk, name),
|
||||||
ae.Error.Error())
|
ae.Error.Error())
|
||||||
} else {
|
} else {
|
||||||
ef.print("%s %s", resourceIDToString(gk, name),
|
ef.print("%s %s", resourceIDToString(gk, name),
|
||||||
strings.ToLower(ae.Operation.String()))
|
strings.ToLower(ae.Operation.String()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef *formatter) FormatStatusEvent(se event.StatusEvent, _ list.Collector) error {
|
func (ef *formatter) FormatStatusEvent(se event.StatusEvent) error {
|
||||||
if se.Type == event.StatusEventResourceUpdate {
|
id := se.Identifier
|
||||||
id := se.Resource.Identifier
|
|
||||||
ef.printResourceStatus(id, se)
|
ef.printResourceStatus(id, se)
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef *formatter) FormatPruneEvent(pe event.PruneEvent, ps *list.PruneStats) error {
|
func (ef *formatter) FormatPruneEvent(pe event.PruneEvent) error {
|
||||||
switch pe.Type {
|
|
||||||
case event.PruneEventCompleted:
|
|
||||||
ef.print("%d resource(s) pruned, %d skipped, %d failed", ps.Pruned, ps.Skipped, ps.Failed)
|
|
||||||
case event.PruneEventResourceUpdate:
|
|
||||||
gk := pe.Identifier.GroupKind
|
gk := pe.Identifier.GroupKind
|
||||||
|
if pe.Error != nil {
|
||||||
|
ef.print("%s prune failed: %s", resourceIDToString(gk, pe.Identifier.Name),
|
||||||
|
pe.Error.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
switch pe.Operation {
|
switch pe.Operation {
|
||||||
case event.Pruned:
|
case event.Pruned:
|
||||||
ef.print("%s pruned", resourceIDToString(gk, pe.Identifier.Name))
|
ef.print("%s pruned", resourceIDToString(gk, pe.Identifier.Name))
|
||||||
case event.PruneSkipped:
|
case event.PruneSkipped:
|
||||||
ef.print("%s prune skipped", resourceIDToString(gk, pe.Identifier.Name))
|
ef.print("%s prune skipped", resourceIDToString(gk, pe.Identifier.Name))
|
||||||
}
|
}
|
||||||
case event.PruneEventFailed:
|
|
||||||
ef.print("%s prune failed: %s", resourceIDToString(pe.Identifier.GroupKind, pe.Identifier.Name),
|
|
||||||
pe.Error.Error())
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef *formatter) FormatDeleteEvent(de event.DeleteEvent, ds *list.DeleteStats) error {
|
func (ef *formatter) FormatDeleteEvent(de event.DeleteEvent) error {
|
||||||
switch de.Type {
|
gk := de.Identifier.GroupKind
|
||||||
case event.DeleteEventCompleted:
|
name := de.Identifier.Name
|
||||||
ef.print("%d resource(s) deleted, %d skipped", ds.Deleted, ds.Skipped)
|
|
||||||
case event.DeleteEventResourceUpdate:
|
if de.Error != nil {
|
||||||
obj := de.Object
|
ef.print("%s deletion failed: %s", resourceIDToString(gk, name),
|
||||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
de.Error.Error())
|
||||||
name := getName(obj)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
switch de.Operation {
|
switch de.Operation {
|
||||||
case event.Deleted:
|
case event.Deleted:
|
||||||
ef.print("%s deleted", resourceIDToString(gvk.GroupKind(), name))
|
ef.print("%s deleted", resourceIDToString(gk, name))
|
||||||
case event.DeleteSkipped:
|
case event.DeleteSkipped:
|
||||||
ef.print("%s delete skipped", resourceIDToString(gvk.GroupKind(), name))
|
ef.print("%s delete skipped", resourceIDToString(gk, name))
|
||||||
}
|
|
||||||
case event.DeleteEventFailed:
|
|
||||||
ef.print("%s deletion failed: %s", resourceIDToString(de.Identifier.GroupKind, de.Identifier.Name),
|
|
||||||
de.Error.Error())
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -109,18 +86,46 @@ func (ef *formatter) FormatErrorEvent(_ event.ErrorEvent) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ef *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) {
|
func (ef *formatter) FormatActionGroupEvent(age event.ActionGroupEvent, ags []event.ActionGroup,
|
||||||
ef.print("%s is %s: %s", resourceIDToString(id.GroupKind, id.Name),
|
as *list.ApplyStats, ps *list.PruneStats, ds *list.DeleteStats, c list.Collector) error {
|
||||||
se.Resource.Status.String(), se.Resource.Message)
|
if age.Action == event.ApplyAction && age.Type == event.Finished {
|
||||||
|
output := fmt.Sprintf("%d resource(s) applied. %d created, %d unchanged, %d configured, %d failed",
|
||||||
|
as.Sum(), as.Created, as.Unchanged, as.Configured, as.Failed)
|
||||||
|
// Only print information about serverside apply if some of the
|
||||||
|
// resources actually were applied serverside.
|
||||||
|
if as.ServersideApplied > 0 {
|
||||||
|
output += fmt.Sprintf(", %d serverside applied", as.ServersideApplied)
|
||||||
|
}
|
||||||
|
ef.print(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if age.Action == event.PruneAction && age.Type == event.Finished {
|
||||||
|
ef.print("%d resource(s) pruned, %d skipped, %d failed", ps.Pruned, ps.Skipped, ps.Failed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if age.Action == event.DeleteAction && age.Type == event.Finished {
|
||||||
|
ef.print("%d resource(s) deleted, %d skipped", ds.Deleted, ds.Skipped)
|
||||||
|
}
|
||||||
|
|
||||||
|
if age.Action == event.WaitAction && age.Type == event.Started {
|
||||||
|
ag, found := list.ActionGroupByName(age.GroupName, ags)
|
||||||
|
if !found {
|
||||||
|
panic(fmt.Errorf("unknown action group name %q", age.GroupName))
|
||||||
|
}
|
||||||
|
for id, se := range c.LatestStatus() {
|
||||||
|
// Only print information about objects that we actually care about
|
||||||
|
// for this wait task.
|
||||||
|
if found := object.ObjMetas(ag.Identifiers).Contains(id); found {
|
||||||
|
ef.printResourceStatus(id, se)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getName(obj runtime.Object) string {
|
func (ef *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) {
|
||||||
if acc, err := meta.Accessor(obj); err == nil {
|
ef.print("%s is %s: %s", resourceIDToString(id.GroupKind, id.Name),
|
||||||
if n := acc.GetName(); len(n) > 0 {
|
se.PollResourceInfo.Status.String(), se.PollResourceInfo.Message)
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "<unknown>"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceIDToString returns the string representation of a GroupKind and a resource name.
|
// resourceIDToString returns the string representation of a GroupKind and a resource name.
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Created,
|
Operation: event.Created,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep created",
|
expected: "deployment.apps/my-dep created",
|
||||||
|
|
@ -41,7 +40,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Configured,
|
Operation: event.Configured,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep configured (preview)",
|
expected: "deployment.apps/my-dep configured (preview)",
|
||||||
|
|
@ -50,7 +48,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunServer,
|
previewStrategy: common.DryRunServer,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Configured,
|
Operation: event.Configured,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
|
Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
|
||||||
},
|
},
|
||||||
expected: "cronjob.batch/my-cron configured (preview-server)",
|
expected: "cronjob.batch/my-cron configured (preview-server)",
|
||||||
|
|
@ -58,42 +55,10 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
"apply event with error should display the error": {
|
"apply event with error should display the error": {
|
||||||
previewStrategy: common.DryRunServer,
|
previewStrategy: common.DryRunServer,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Failed,
|
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
Error: fmt.Errorf("this is a test error"),
|
Error: fmt.Errorf("this is a test error"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep failed: this is a test error (preview-server)",
|
expected: "deployment.apps/my-dep apply failed: this is a test error (preview-server)",
|
||||||
},
|
|
||||||
"completed event": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.ApplyEvent{
|
|
||||||
Type: event.ApplyEventCompleted,
|
|
||||||
},
|
|
||||||
applyStats: &list.ApplyStats{
|
|
||||||
ServersideApplied: 1,
|
|
||||||
},
|
|
||||||
statusCollector: &fakeCollector{
|
|
||||||
m: map[object.ObjMetadata]event.StatusEvent{
|
|
||||||
object.ObjMetadata{ //nolint:gofmt
|
|
||||||
GroupKind: schema.GroupKind{
|
|
||||||
Group: "apps",
|
|
||||||
Kind: "Deployment",
|
|
||||||
},
|
|
||||||
Namespace: "foo",
|
|
||||||
Name: "my-dep",
|
|
||||||
}: {
|
|
||||||
Resource: &pollevent.ResourceStatus{
|
|
||||||
Status: status.CurrentStatus,
|
|
||||||
Message: "Resource is Current",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: `
|
|
||||||
1 resource(s) applied. 0 created, 0 unchanged, 0 configured, 0 failed, 1 serverside applied
|
|
||||||
deployment.apps/my-dep is Current: Resource is Current
|
|
||||||
`,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +66,7 @@ deployment.apps/my-dep is Current: Resource is Current
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatApplyEvent(tc.event, tc.applyStats, tc.statusCollector)
|
err := formatter.FormatApplyEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
||||||
|
|
@ -119,8 +84,15 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
|
||||||
"resource update with Current status": {
|
"resource update with Current status": {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.StatusEvent{
|
event: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
Identifier: object.ObjMetadata{
|
||||||
Resource: &pollevent.ResourceStatus{
|
GroupKind: schema.GroupKind{
|
||||||
|
Group: "apps",
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
Namespace: "foo",
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
PollResourceInfo: &pollevent.ResourceStatus{
|
||||||
Identifier: object.ObjMetadata{
|
Identifier: object.ObjMetadata{
|
||||||
GroupKind: schema.GroupKind{
|
GroupKind: schema.GroupKind{
|
||||||
Group: "apps",
|
Group: "apps",
|
||||||
|
|
@ -141,7 +113,7 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatStatusEvent(tc.event, tc.statusCollector)
|
err := formatter.FormatStatusEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
||||||
|
|
@ -160,7 +132,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.PruneEvent{
|
event: event.PruneEvent{
|
||||||
Operation: event.Pruned,
|
Operation: event.Pruned,
|
||||||
Type: event.PruneEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep pruned",
|
expected: "deployment.apps/my-dep pruned",
|
||||||
|
|
@ -169,7 +140,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.PruneEvent{
|
event: event.PruneEvent{
|
||||||
Operation: event.PruneSkipped,
|
Operation: event.PruneSkipped,
|
||||||
Type: event.PruneEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep prune skipped (preview)",
|
expected: "deployment.apps/my-dep prune skipped (preview)",
|
||||||
|
|
@ -177,30 +147,18 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
"resource with prune error": {
|
"resource with prune error": {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.PruneEvent{
|
event: event.PruneEvent{
|
||||||
Type: event.PruneEventFailed,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
Error: fmt.Errorf("this is a test"),
|
Error: fmt.Errorf("this is a test"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep prune failed: this is a test",
|
expected: "deployment.apps/my-dep prune failed: this is a test",
|
||||||
},
|
},
|
||||||
"prune event with completed status": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.PruneEvent{
|
|
||||||
Type: event.PruneEventCompleted,
|
|
||||||
},
|
|
||||||
pruneStats: &list.PruneStats{
|
|
||||||
Pruned: 1,
|
|
||||||
Skipped: 2,
|
|
||||||
},
|
|
||||||
expected: "1 resource(s) pruned, 2 skipped, 0 failed",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatPruneEvent(tc.event, tc.pruneStats)
|
err := formatter.FormatPruneEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
||||||
|
|
@ -220,7 +178,7 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.DeleteEvent{
|
event: event.DeleteEvent{
|
||||||
Operation: event.Deleted,
|
Operation: event.Deleted,
|
||||||
Type: event.DeleteEventResourceUpdate,
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
Object: createObject("apps", "Deployment", "default", "my-dep"),
|
Object: createObject("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep deleted",
|
expected: "deployment.apps/my-dep deleted",
|
||||||
|
|
@ -229,7 +187,7 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.DeleteEvent{
|
event: event.DeleteEvent{
|
||||||
Operation: event.DeleteSkipped,
|
Operation: event.DeleteSkipped,
|
||||||
Type: event.DeleteEventResourceUpdate,
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
Object: createObject("apps", "Deployment", "", "my-dep"),
|
Object: createObject("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep delete skipped (preview)",
|
expected: "deployment.apps/my-dep delete skipped (preview)",
|
||||||
|
|
@ -237,31 +195,19 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
"resource with delete error": {
|
"resource with delete error": {
|
||||||
previewStrategy: common.DryRunServer,
|
previewStrategy: common.DryRunServer,
|
||||||
event: event.DeleteEvent{
|
event: event.DeleteEvent{
|
||||||
Type: event.DeleteEventFailed,
|
|
||||||
Object: createObject("apps", "Deployment", "", "my-dep"),
|
Object: createObject("apps", "Deployment", "", "my-dep"),
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
Error: fmt.Errorf("this is a test"),
|
Error: fmt.Errorf("this is a test"),
|
||||||
},
|
},
|
||||||
expected: "deployment.apps/my-dep deletion failed: this is a test (preview-server)",
|
expected: "deployment.apps/my-dep deletion failed: this is a test (preview-server)",
|
||||||
},
|
},
|
||||||
"delete event with completed status": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.DeleteEvent{
|
|
||||||
Type: event.DeleteEventCompleted,
|
|
||||||
},
|
|
||||||
deleteStats: &list.DeleteStats{
|
|
||||||
Deleted: 1,
|
|
||||||
Skipped: 2,
|
|
||||||
},
|
|
||||||
expected: "1 resource(s) deleted, 2 skipped",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatDeleteEvent(tc.event, tc.deleteStats)
|
err := formatter.FormatDeleteEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(out.String()))
|
||||||
|
|
@ -292,11 +238,3 @@ func createIdentifier(group, kind, namespace, name string) object.ObjMetadata {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeCollector struct {
|
|
||||||
m map[object.ObjMetadata]event.StatusEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeCollector) LatestStatus() map[object.ObjMetadata]event.StatusEvent {
|
|
||||||
return f.m
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,56 @@ type formatter struct {
|
||||||
ioStreams genericclioptions.IOStreams
|
ioStreams genericclioptions.IOStreams
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats, c list.Collector) error {
|
func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent) error {
|
||||||
switch ae.Type {
|
eventInfo := jf.baseResourceEvent(ae.Identifier)
|
||||||
case event.ApplyEventCompleted:
|
if ae.Error != nil {
|
||||||
|
eventInfo["error"] = ae.Error.Error()
|
||||||
|
return jf.printEvent("apply", "resourceFailed", eventInfo)
|
||||||
|
}
|
||||||
|
eventInfo["operation"] = ae.Operation.String()
|
||||||
|
return jf.printEvent("apply", "resourceApplied", eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) FormatStatusEvent(se event.StatusEvent) error {
|
||||||
|
return jf.printResourceStatus(se)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) printResourceStatus(se event.StatusEvent) error {
|
||||||
|
eventInfo := jf.baseResourceEvent(se.Identifier)
|
||||||
|
eventInfo["status"] = se.PollResourceInfo.Status.String()
|
||||||
|
eventInfo["message"] = se.PollResourceInfo.Message
|
||||||
|
return jf.printEvent("status", "resourceStatus", eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) FormatPruneEvent(pe event.PruneEvent) error {
|
||||||
|
eventInfo := jf.baseResourceEvent(pe.Identifier)
|
||||||
|
if pe.Error != nil {
|
||||||
|
eventInfo["error"] = pe.Error.Error()
|
||||||
|
return jf.printEvent("prune", "resourceFailed", eventInfo)
|
||||||
|
}
|
||||||
|
eventInfo["operation"] = pe.Operation.String()
|
||||||
|
return jf.printEvent("prune", "resourcePruned", eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) FormatDeleteEvent(de event.DeleteEvent) error {
|
||||||
|
eventInfo := jf.baseResourceEvent(de.Identifier)
|
||||||
|
if de.Error != nil {
|
||||||
|
eventInfo["error"] = de.Error.Error()
|
||||||
|
return jf.printEvent("delete", "resourceFailed", eventInfo)
|
||||||
|
}
|
||||||
|
eventInfo["operation"] = de.Operation.String()
|
||||||
|
return jf.printEvent("delete", "resourceDeleted", eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) FormatErrorEvent(ee event.ErrorEvent) error {
|
||||||
|
return jf.printEvent("error", "error", map[string]interface{}{
|
||||||
|
"error": ee.Err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jf *formatter) FormatActionGroupEvent(age event.ActionGroupEvent, ags []event.ActionGroup,
|
||||||
|
as *list.ApplyStats, ps *list.PruneStats, ds *list.DeleteStats, c list.Collector) error {
|
||||||
|
if age.Action == event.ApplyAction && age.Type == event.Finished {
|
||||||
if err := jf.printEvent("apply", "completed", map[string]interface{}{
|
if err := jf.printEvent("apply", "completed", map[string]interface{}{
|
||||||
"count": as.Sum(),
|
"count": as.Sum(),
|
||||||
"createdCount": as.Created,
|
"createdCount": as.Created,
|
||||||
|
|
@ -41,112 +88,47 @@ func (jf *formatter) FormatApplyEvent(ae event.ApplyEvent, as *list.ApplyStats,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for id, se := range c.LatestStatus() {
|
|
||||||
if err := jf.printResourceStatus(id, se); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case event.ApplyEventResourceUpdate:
|
|
||||||
gk := ae.Identifier.GroupKind
|
|
||||||
eventInfo := map[string]interface{}{
|
|
||||||
"group": gk.Group,
|
|
||||||
"kind": gk.Kind,
|
|
||||||
"namespace": ae.Identifier.Namespace,
|
|
||||||
"name": ae.Identifier.Name,
|
|
||||||
"operation": ae.Operation.String(),
|
|
||||||
}
|
|
||||||
if ae.Error != nil {
|
|
||||||
eventInfo["error"] = ae.Error.Error()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return jf.printEvent("apply", "resourceApplied", eventInfo)
|
if age.Action == event.PruneAction && age.Type == event.Finished {
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jf *formatter) FormatStatusEvent(se event.StatusEvent, _ list.Collector) error {
|
|
||||||
if se.Type == event.StatusEventResourceUpdate {
|
|
||||||
id := se.Resource.Identifier
|
|
||||||
return jf.printResourceStatus(id, se)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jf *formatter) printResourceStatus(id object.ObjMetadata, se event.StatusEvent) error {
|
|
||||||
return jf.printEvent("status", "resourceStatus",
|
|
||||||
map[string]interface{}{
|
|
||||||
"group": id.GroupKind.Group,
|
|
||||||
"kind": id.GroupKind.Kind,
|
|
||||||
"namespace": id.Namespace,
|
|
||||||
"name": id.Name,
|
|
||||||
"status": se.Resource.Status.String(),
|
|
||||||
"message": se.Resource.Message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jf *formatter) FormatPruneEvent(pe event.PruneEvent, ps *list.PruneStats) error {
|
|
||||||
switch pe.Type {
|
|
||||||
case event.PruneEventCompleted:
|
|
||||||
return jf.printEvent("prune", "completed", map[string]interface{}{
|
return jf.printEvent("prune", "completed", map[string]interface{}{
|
||||||
"pruned": ps.Pruned,
|
"pruned": ps.Pruned,
|
||||||
"skipped": ps.Skipped,
|
"skipped": ps.Skipped,
|
||||||
})
|
})
|
||||||
case event.PruneEventResourceUpdate:
|
|
||||||
gk := pe.Identifier.GroupKind
|
|
||||||
return jf.printEvent("prune", "resourcePruned", map[string]interface{}{
|
|
||||||
"group": gk.Group,
|
|
||||||
"kind": gk.Kind,
|
|
||||||
"namespace": pe.Identifier.Namespace,
|
|
||||||
"name": pe.Identifier.Name,
|
|
||||||
"operation": pe.Operation.String(),
|
|
||||||
})
|
|
||||||
case event.PruneEventFailed:
|
|
||||||
gk := pe.Identifier.GroupKind
|
|
||||||
return jf.printEvent("prune", "resourceFailed", map[string]interface{}{
|
|
||||||
"group": gk.Group,
|
|
||||||
"kind": gk.Kind,
|
|
||||||
"namespace": pe.Identifier.Namespace,
|
|
||||||
"name": pe.Identifier.Name,
|
|
||||||
"error": pe.Error.Error(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jf *formatter) FormatDeleteEvent(de event.DeleteEvent, ds *list.DeleteStats) error {
|
if age.Action == event.DeleteAction && age.Type == event.Finished {
|
||||||
switch de.Type {
|
|
||||||
case event.DeleteEventCompleted:
|
|
||||||
return jf.printEvent("delete", "completed", map[string]interface{}{
|
return jf.printEvent("delete", "completed", map[string]interface{}{
|
||||||
"deleted": ds.Deleted,
|
"deleted": ds.Deleted,
|
||||||
"skipped": ds.Skipped,
|
"skipped": ds.Skipped,
|
||||||
})
|
})
|
||||||
case event.DeleteEventResourceUpdate:
|
}
|
||||||
gk := de.Identifier.GroupKind
|
|
||||||
return jf.printEvent("delete", "resourceDeleted", map[string]interface{}{
|
if age.Action == event.WaitAction && age.Type == event.Started {
|
||||||
"group": gk.Group,
|
ag, found := list.ActionGroupByName(age.GroupName, ags)
|
||||||
"kind": gk.Kind,
|
if !found {
|
||||||
"namespace": de.Identifier.Namespace,
|
panic(fmt.Errorf("unknown action group name %q", age.GroupName))
|
||||||
"name": de.Identifier.Name,
|
}
|
||||||
"operation": de.Operation.String(),
|
for id, se := range c.LatestStatus() {
|
||||||
})
|
// Only print information about objects that we actually care about
|
||||||
case event.DeleteEventFailed:
|
// for this wait task.
|
||||||
gk := de.Identifier.GroupKind
|
if found := object.ObjMetas(ag.Identifiers).Contains(id); found {
|
||||||
return jf.printEvent("delete", "resourceFailed", map[string]interface{}{
|
if err := jf.printResourceStatus(se); err != nil {
|
||||||
"group": gk.Group,
|
return err
|
||||||
"kind": gk.Kind,
|
}
|
||||||
"namespace": de.Identifier.Namespace,
|
}
|
||||||
"name": de.Identifier.Name,
|
}
|
||||||
"error": de.Error.Error(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jf *formatter) FormatErrorEvent(ee event.ErrorEvent) error {
|
func (jf *formatter) baseResourceEvent(identifier object.ObjMetadata) map[string]interface{} {
|
||||||
return jf.printEvent("error", "error", map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"error": ee.Err.Error(),
|
"group": identifier.GroupKind.Group,
|
||||||
})
|
"kind": identifier.GroupKind.Kind,
|
||||||
|
"namespace": identifier.Namespace,
|
||||||
|
"name": identifier.Name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jf *formatter) printEvent(t, eventType string, content map[string]interface{}) error {
|
func (jf *formatter) printEvent(t, eventType string, content map[string]interface{}) error {
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,12 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
previewStrategy common.DryRunStrategy
|
previewStrategy common.DryRunStrategy
|
||||||
event event.ApplyEvent
|
event event.ApplyEvent
|
||||||
applyStats *list.ApplyStats
|
|
||||||
statusCollector list.Collector
|
|
||||||
expected []map[string]interface{}
|
expected []map[string]interface{}
|
||||||
}{
|
}{
|
||||||
"resource created without dryrun": {
|
"resource created without dryrun": {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Created,
|
Operation: event.Created,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]interface{}{
|
||||||
|
|
@ -51,7 +48,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Configured,
|
Operation: event.Configured,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]interface{}{
|
||||||
|
|
@ -71,7 +67,6 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunServer,
|
previewStrategy: common.DryRunServer,
|
||||||
event: event.ApplyEvent{
|
event: event.ApplyEvent{
|
||||||
Operation: event.Configured,
|
Operation: event.Configured,
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
|
Identifier: createIdentifier("batch", "CronJob", "foo", "my-cron"),
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]interface{}{
|
||||||
|
|
@ -87,63 +82,13 @@ func TestFormatter_FormatApplyEvent(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"completed event": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.ApplyEvent{
|
|
||||||
Type: event.ApplyEventCompleted,
|
|
||||||
},
|
|
||||||
applyStats: &list.ApplyStats{
|
|
||||||
ServersideApplied: 1,
|
|
||||||
},
|
|
||||||
statusCollector: &fakeCollector{
|
|
||||||
m: map[object.ObjMetadata]event.StatusEvent{
|
|
||||||
object.ObjMetadata{ //nolint:gofmt
|
|
||||||
GroupKind: schema.GroupKind{
|
|
||||||
Group: "apps",
|
|
||||||
Kind: "Deployment",
|
|
||||||
},
|
|
||||||
Namespace: "foo",
|
|
||||||
Name: "my-dep",
|
|
||||||
}: {
|
|
||||||
Resource: &pollevent.ResourceStatus{
|
|
||||||
Status: status.CurrentStatus,
|
|
||||||
Message: "Resource is Current",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"configuredCount": 0,
|
|
||||||
"count": 1,
|
|
||||||
"createdCount": 0,
|
|
||||||
"eventType": "completed",
|
|
||||||
"failedCount": 0,
|
|
||||||
"serverSideCount": 1,
|
|
||||||
"type": "apply",
|
|
||||||
"unchangedCount": 0,
|
|
||||||
"timestamp": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"eventType": "resourceStatus",
|
|
||||||
"group": "apps",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"message": "Resource is Current",
|
|
||||||
"name": "my-dep",
|
|
||||||
"namespace": "foo",
|
|
||||||
"status": "Current",
|
|
||||||
"timestamp": "",
|
|
||||||
"type": "status",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatApplyEvent(tc.event, tc.applyStats, tc.statusCollector)
|
err := formatter.FormatApplyEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
objects := strings.Split(strings.TrimSpace(out.String()), "\n")
|
objects := strings.Split(strings.TrimSpace(out.String()), "\n")
|
||||||
|
|
@ -162,14 +107,20 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
previewStrategy common.DryRunStrategy
|
previewStrategy common.DryRunStrategy
|
||||||
event event.StatusEvent
|
event event.StatusEvent
|
||||||
statusCollector list.Collector
|
|
||||||
expected map[string]interface{}
|
expected map[string]interface{}
|
||||||
}{
|
}{
|
||||||
"resource update with Current status": {
|
"resource update with Current status": {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.StatusEvent{
|
event: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
Identifier: object.ObjMetadata{
|
||||||
Resource: &pollevent.ResourceStatus{
|
GroupKind: schema.GroupKind{
|
||||||
|
Group: "apps",
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
Namespace: "foo",
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
PollResourceInfo: &pollevent.ResourceStatus{
|
||||||
Identifier: object.ObjMetadata{
|
Identifier: object.ObjMetadata{
|
||||||
GroupKind: schema.GroupKind{
|
GroupKind: schema.GroupKind{
|
||||||
Group: "apps",
|
Group: "apps",
|
||||||
|
|
@ -200,7 +151,7 @@ func TestFormatter_FormatStatusEvent(t *testing.T) {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatStatusEvent(tc.event, tc.statusCollector)
|
err := formatter.FormatStatusEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assertOutput(t, tc.expected, out.String())
|
assertOutput(t, tc.expected, out.String())
|
||||||
|
|
@ -219,7 +170,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.PruneEvent{
|
event: event.PruneEvent{
|
||||||
Operation: event.Pruned,
|
Operation: event.Pruned,
|
||||||
Type: event.PruneEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
|
|
@ -237,7 +187,6 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.PruneEvent{
|
event: event.PruneEvent{
|
||||||
Operation: event.PruneSkipped,
|
Operation: event.PruneSkipped,
|
||||||
Type: event.PruneEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
|
|
@ -251,30 +200,13 @@ func TestFormatter_FormatPruneEvent(t *testing.T) {
|
||||||
"type": "prune",
|
"type": "prune",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"prune event with completed status": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.PruneEvent{
|
|
||||||
Type: event.PruneEventCompleted,
|
|
||||||
},
|
|
||||||
pruneStats: &list.PruneStats{
|
|
||||||
Pruned: 1,
|
|
||||||
Skipped: 2,
|
|
||||||
},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"eventType": "completed",
|
|
||||||
"skipped": 2,
|
|
||||||
"pruned": 1,
|
|
||||||
"timestamp": "",
|
|
||||||
"type": "prune",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatPruneEvent(tc.event, tc.pruneStats)
|
err := formatter.FormatPruneEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assertOutput(t, tc.expected, out.String())
|
assertOutput(t, tc.expected, out.String())
|
||||||
|
|
@ -294,7 +226,6 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunNone,
|
previewStrategy: common.DryRunNone,
|
||||||
event: event.DeleteEvent{
|
event: event.DeleteEvent{
|
||||||
Operation: event.Deleted,
|
Operation: event.Deleted,
|
||||||
Type: event.DeleteEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "default", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
|
|
@ -312,7 +243,6 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
previewStrategy: common.DryRunClient,
|
previewStrategy: common.DryRunClient,
|
||||||
event: event.DeleteEvent{
|
event: event.DeleteEvent{
|
||||||
Operation: event.DeleteSkipped,
|
Operation: event.DeleteSkipped,
|
||||||
Type: event.DeleteEventResourceUpdate,
|
|
||||||
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
Identifier: createIdentifier("apps", "Deployment", "", "my-dep"),
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
|
|
@ -326,30 +256,13 @@ func TestFormatter_FormatDeleteEvent(t *testing.T) {
|
||||||
"type": "delete",
|
"type": "delete",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"delete event with completed status": {
|
|
||||||
previewStrategy: common.DryRunNone,
|
|
||||||
event: event.DeleteEvent{
|
|
||||||
Type: event.DeleteEventCompleted,
|
|
||||||
},
|
|
||||||
deleteStats: &list.DeleteStats{
|
|
||||||
Deleted: 1,
|
|
||||||
Skipped: 2,
|
|
||||||
},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"deleted": 1,
|
|
||||||
"eventType": "completed",
|
|
||||||
"skipped": 2,
|
|
||||||
"timestamp": "",
|
|
||||||
"type": "delete",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
ioStreams, _, out, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
|
||||||
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
formatter := NewFormatter(ioStreams, tc.previewStrategy)
|
||||||
err := formatter.FormatDeleteEvent(tc.event, tc.deleteStats)
|
err := formatter.FormatDeleteEvent(tc.event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assertOutput(t, tc.expected, out.String())
|
assertOutput(t, tc.expected, out.String())
|
||||||
|
|
@ -394,11 +307,3 @@ func createIdentifier(group, kind, namespace, name string) object.ObjMetadata {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeCollector struct {
|
|
||||||
m map[object.ObjMetadata]event.StatusEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeCollector) LatestStatus() map[object.ObjMetadata]event.StatusEvent {
|
|
||||||
return f.m
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/print/table"
|
"sigs.k8s.io/cli-utils/pkg/print/table"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newResourceStateCollector(resourceGroups []event.ResourceGroup) *ResourceStateCollector {
|
func newResourceStateCollector(resourceGroups []event.ActionGroup) *ResourceStateCollector {
|
||||||
resourceInfos := make(map[object.ObjMetadata]*ResourceInfo)
|
resourceInfos := make(map[object.ObjMetadata]*ResourceInfo)
|
||||||
for _, group := range resourceGroups {
|
for _, group := range resourceGroups {
|
||||||
action := group.Action
|
action := group.Action
|
||||||
|
|
@ -70,15 +70,15 @@ type ResourceInfo struct {
|
||||||
|
|
||||||
// ApplyOpResult contains the result after
|
// ApplyOpResult contains the result after
|
||||||
// a resource has been applied to the cluster.
|
// a resource has been applied to the cluster.
|
||||||
ApplyOpResult *event.ApplyEventOperation
|
ApplyOpResult event.ApplyEventOperation
|
||||||
|
|
||||||
// PruneOpResult contains the result after
|
// PruneOpResult contains the result after
|
||||||
// a prune operation on a resource
|
// a prune operation on a resource
|
||||||
PruneOpResult *event.PruneEventOperation
|
PruneOpResult event.PruneEventOperation
|
||||||
|
|
||||||
// DeleteOpResult contains the result after
|
// DeleteOpResult contains the result after
|
||||||
// a delete operation on a resource
|
// a delete operation on a resource
|
||||||
DeleteOpResult *event.DeleteEventOperation
|
DeleteOpResult event.DeleteEventOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifier returns the identifier for the given resource.
|
// Identifier returns the identifier for the given resource.
|
||||||
|
|
@ -182,25 +182,17 @@ func (r *ResourceStateCollector) processEvent(ev event.Event) error {
|
||||||
// processStatusEvent handles events pertaining to a status
|
// processStatusEvent handles events pertaining to a status
|
||||||
// update for a resource.
|
// update for a resource.
|
||||||
func (r *ResourceStateCollector) processStatusEvent(e event.StatusEvent) {
|
func (r *ResourceStateCollector) processStatusEvent(e event.StatusEvent) {
|
||||||
if e.Type == event.StatusEventResourceUpdate {
|
|
||||||
klog.V(7).Infoln("processing status event")
|
klog.V(7).Infoln("processing status event")
|
||||||
resource := e.Resource
|
previous, found := r.resourceInfos[e.Identifier]
|
||||||
if resource == nil {
|
|
||||||
klog.V(4).Infoln("status event missing Resource field; no processing")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
previous, found := r.resourceInfos[resource.Identifier]
|
|
||||||
if !found {
|
if !found {
|
||||||
klog.V(4).Infof("%s status event not found in ResourceInfos; no processing", resource.Identifier)
|
klog.V(4).Infof("%s status event not found in ResourceInfos; no processing", e.Identifier)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
previous.resourceStatus = e.Resource
|
previous.resourceStatus = e.PollResourceInfo
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processApplyEvent handles events relating to apply operations
|
// processApplyEvent handles events relating to apply operations
|
||||||
func (r *ResourceStateCollector) processApplyEvent(e event.ApplyEvent) {
|
func (r *ResourceStateCollector) processApplyEvent(e event.ApplyEvent) {
|
||||||
if e.Type == event.ApplyEventResourceUpdate {
|
|
||||||
identifier := e.Identifier
|
identifier := e.Identifier
|
||||||
klog.V(7).Infof("processing apply event for %s", identifier)
|
klog.V(7).Infof("processing apply event for %s", identifier)
|
||||||
previous, found := r.resourceInfos[identifier]
|
previous, found := r.resourceInfos[identifier]
|
||||||
|
|
@ -208,13 +200,11 @@ func (r *ResourceStateCollector) processApplyEvent(e event.ApplyEvent) {
|
||||||
klog.V(4).Infof("%s apply event not found in ResourceInfos; no processing", identifier)
|
klog.V(4).Infof("%s apply event not found in ResourceInfos; no processing", identifier)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
previous.ApplyOpResult = &e.Operation
|
previous.ApplyOpResult = e.Operation
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processPruneEvent handles event related to prune operations.
|
// processPruneEvent handles event related to prune operations.
|
||||||
func (r *ResourceStateCollector) processPruneEvent(e event.PruneEvent) {
|
func (r *ResourceStateCollector) processPruneEvent(e event.PruneEvent) {
|
||||||
if e.Type == event.PruneEventResourceUpdate {
|
|
||||||
identifier := e.Identifier
|
identifier := e.Identifier
|
||||||
klog.V(7).Infof("processing prune event for %s", identifier)
|
klog.V(7).Infof("processing prune event for %s", identifier)
|
||||||
previous, found := r.resourceInfos[identifier]
|
previous, found := r.resourceInfos[identifier]
|
||||||
|
|
@ -222,8 +212,7 @@ func (r *ResourceStateCollector) processPruneEvent(e event.PruneEvent) {
|
||||||
klog.V(4).Infof("%s prune event not found in ResourceInfos; no processing", identifier)
|
klog.V(4).Infof("%s prune event not found in ResourceInfos; no processing", identifier)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
previous.PruneOpResult = &e.Operation
|
previous.PruneOpResult = e.Operation
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceState contains the latest state for all the resources.
|
// ResourceState contains the latest state for all the resources.
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,15 @@ const testMessage = "test message for ResourceStatus"
|
||||||
|
|
||||||
func TestResourceStateCollector_New(t *testing.T) {
|
func TestResourceStateCollector_New(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
resourceGroups []event.ResourceGroup
|
resourceGroups []event.ActionGroup
|
||||||
resourceInfos map[object.ObjMetadata]*ResourceInfo
|
resourceInfos map[object.ObjMetadata]*ResourceInfo
|
||||||
}{
|
}{
|
||||||
"no resources": {
|
"no resources": {
|
||||||
resourceGroups: []event.ResourceGroup{},
|
resourceGroups: []event.ActionGroup{},
|
||||||
resourceInfos: map[object.ObjMetadata]*ResourceInfo{},
|
resourceInfos: map[object.ObjMetadata]*ResourceInfo{},
|
||||||
},
|
},
|
||||||
"several resources for apply": {
|
"several resources for apply": {
|
||||||
resourceGroups: []event.ResourceGroup{
|
resourceGroups: []event.ActionGroup{
|
||||||
{
|
{
|
||||||
Action: event.ApplyAction,
|
Action: event.ApplyAction,
|
||||||
Identifiers: []object.ObjMetadata{
|
Identifiers: []object.ObjMetadata{
|
||||||
|
|
@ -61,7 +61,7 @@ func TestResourceStateCollector_New(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"several resources for prune": {
|
"several resources for prune": {
|
||||||
resourceGroups: []event.ResourceGroup{
|
resourceGroups: []event.ActionGroup{
|
||||||
{
|
{
|
||||||
Action: event.ApplyAction,
|
Action: event.ApplyAction,
|
||||||
Identifiers: []object.ObjMetadata{
|
Identifiers: []object.ObjMetadata{
|
||||||
|
|
@ -104,39 +104,30 @@ func TestResourceStateCollector_New(t *testing.T) {
|
||||||
|
|
||||||
func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
|
func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
resourceGroups []event.ResourceGroup
|
resourceGroups []event.ActionGroup
|
||||||
statusEvent event.StatusEvent
|
statusEvent event.StatusEvent
|
||||||
}{
|
}{
|
||||||
"nil StatusEvent.Resource does not crash": {
|
"nil StatusEvent.Resource does not crash": {
|
||||||
resourceGroups: []event.ResourceGroup{},
|
resourceGroups: []event.ActionGroup{},
|
||||||
statusEvent: event.StatusEvent{
|
statusEvent: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
|
||||||
Resource: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"type StatusEventCompleted does nothing": {
|
|
||||||
resourceGroups: []event.ResourceGroup{},
|
|
||||||
statusEvent: event.StatusEvent{
|
|
||||||
Type: event.StatusEventCompleted,
|
|
||||||
Resource: nil,
|
Resource: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"unfound Resource identifier does not crash": {
|
"unfound Resource identifier does not crash": {
|
||||||
resourceGroups: []event.ResourceGroup{
|
resourceGroups: []event.ActionGroup{
|
||||||
{
|
{
|
||||||
Action: event.ApplyAction,
|
Action: event.ApplyAction,
|
||||||
Identifiers: []object.ObjMetadata{depID},
|
Identifiers: []object.ObjMetadata{depID},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusEvent: event.StatusEvent{
|
statusEvent: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
PollResourceInfo: &pe.ResourceStatus{
|
||||||
Resource: &pe.ResourceStatus{
|
|
||||||
Identifier: customID, // Does not match identifier in resourceGroups
|
Identifier: customID, // Does not match identifier in resourceGroups
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"basic status event for applying two resources updates resourceStatus": {
|
"basic status event for applying two resources updates resourceStatus": {
|
||||||
resourceGroups: []event.ResourceGroup{
|
resourceGroups: []event.ActionGroup{
|
||||||
{
|
{
|
||||||
Action: event.ApplyAction,
|
Action: event.ApplyAction,
|
||||||
Identifiers: []object.ObjMetadata{
|
Identifiers: []object.ObjMetadata{
|
||||||
|
|
@ -145,15 +136,14 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusEvent: event.StatusEvent{
|
statusEvent: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
PollResourceInfo: &pe.ResourceStatus{
|
||||||
Resource: &pe.ResourceStatus{
|
|
||||||
Identifier: depID,
|
Identifier: depID,
|
||||||
Message: testMessage,
|
Message: testMessage,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"several resources for prune": {
|
"several resources for prune": {
|
||||||
resourceGroups: []event.ResourceGroup{
|
resourceGroups: []event.ActionGroup{
|
||||||
{
|
{
|
||||||
Action: event.ApplyAction,
|
Action: event.ApplyAction,
|
||||||
Identifiers: []object.ObjMetadata{
|
Identifiers: []object.ObjMetadata{
|
||||||
|
|
@ -168,8 +158,7 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusEvent: event.StatusEvent{
|
statusEvent: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
PollResourceInfo: &pe.ResourceStatus{
|
||||||
Resource: &pe.ResourceStatus{
|
|
||||||
Identifier: depID,
|
Identifier: depID,
|
||||||
Message: testMessage,
|
Message: testMessage,
|
||||||
},
|
},
|
||||||
|
|
@ -186,7 +175,7 @@ func TestResourceStateCollector_ProcessStatusEvent(t *testing.T) {
|
||||||
resourceInfo, found := rsc.resourceInfos[id]
|
resourceInfo, found := rsc.resourceInfos[id]
|
||||||
if found {
|
if found {
|
||||||
// Validate the ResourceStatus was set from StatusEvent
|
// Validate the ResourceStatus was set from StatusEvent
|
||||||
if resourceInfo.resourceStatus != tc.statusEvent.Resource {
|
if resourceInfo.resourceStatus != tc.statusEvent.PollResourceInfo {
|
||||||
t.Errorf("status event not processed for %s", id)
|
t.Errorf("status event not processed for %s", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,5 +188,5 @@ func getID(e event.StatusEvent) (object.ObjMetadata, bool) {
|
||||||
if e.Resource == nil {
|
if e.Resource == nil {
|
||||||
return object.ObjMetadata{}, false
|
return object.ObjMetadata{}, false
|
||||||
}
|
}
|
||||||
return e.Resource.Identifier, true
|
return e.Identifier, true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func (t *Printer) Print(ch <-chan event.Event, _ common.DryRunStrategy) error {
|
||||||
}
|
}
|
||||||
// Create a new collector and initialize it with the resources
|
// Create a new collector and initialize it with the resources
|
||||||
// we are interested in.
|
// we are interested in.
|
||||||
coll := newResourceStateCollector(initEvent.ResourceGroups)
|
coll := newResourceStateCollector(initEvent.ActionGroups)
|
||||||
|
|
||||||
stop := make(chan struct{})
|
stop := make(chan struct{})
|
||||||
|
|
||||||
|
|
@ -89,11 +89,11 @@ var (
|
||||||
var text string
|
var text string
|
||||||
switch resInfo.ResourceAction {
|
switch resInfo.ResourceAction {
|
||||||
case event.ApplyAction:
|
case event.ApplyAction:
|
||||||
if resInfo.ApplyOpResult != nil {
|
if resInfo.ApplyOpResult != event.ApplyUnspecified {
|
||||||
text = resInfo.ApplyOpResult.String()
|
text = resInfo.ApplyOpResult.String()
|
||||||
}
|
}
|
||||||
case event.PruneAction:
|
case event.PruneAction:
|
||||||
if resInfo.PruneOpResult != nil {
|
if resInfo.PruneOpResult != event.PruneUnspecified {
|
||||||
text = resInfo.PruneOpResult.String()
|
text = resInfo.PruneOpResult.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func TestActionColumnDef(t *testing.T) {
|
||||||
"applied": {
|
"applied": {
|
||||||
resource: &ResourceInfo{
|
resource: &ResourceInfo{
|
||||||
ResourceAction: event.ApplyAction,
|
ResourceAction: event.ApplyAction,
|
||||||
ApplyOpResult: &createdOpResult,
|
ApplyOpResult: createdOpResult,
|
||||||
},
|
},
|
||||||
columnWidth: 15,
|
columnWidth: 15,
|
||||||
expectedOutput: "Created",
|
expectedOutput: "Created",
|
||||||
|
|
@ -43,7 +43,7 @@ func TestActionColumnDef(t *testing.T) {
|
||||||
"pruned": {
|
"pruned": {
|
||||||
resource: &ResourceInfo{
|
resource: &ResourceInfo{
|
||||||
ResourceAction: event.PruneAction,
|
ResourceAction: event.PruneAction,
|
||||||
PruneOpResult: &prunedOpResult,
|
PruneOpResult: prunedOpResult,
|
||||||
},
|
},
|
||||||
columnWidth: 15,
|
columnWidth: 15,
|
||||||
expectedOutput: "Pruned",
|
expectedOutput: "Pruned",
|
||||||
|
|
@ -51,7 +51,7 @@ func TestActionColumnDef(t *testing.T) {
|
||||||
"trimmed output": {
|
"trimmed output": {
|
||||||
resource: &ResourceInfo{
|
resource: &ResourceInfo{
|
||||||
ResourceAction: event.ApplyAction,
|
ResourceAction: event.ApplyAction,
|
||||||
ApplyOpResult: &createdOpResult,
|
ApplyOpResult: createdOpResult,
|
||||||
},
|
},
|
||||||
columnWidth: 5,
|
columnWidth: 5,
|
||||||
expectedOutput: "Creat",
|
expectedOutput: "Creat",
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,6 @@ Use the `kapply` binary in `MYGOBIN` to apply both the CRD and the CR.
|
||||||
```
|
```
|
||||||
kapply apply $BASE --reconcile-timeout=1m > $OUTPUT/status
|
kapply apply $BASE --reconcile-timeout=1m > $OUTPUT/status
|
||||||
|
|
||||||
expectedOutputLine "customresourcedefinition.apiextensions.k8s.io/foos.custom.io is Current: CRD is established"
|
|
||||||
|
|
||||||
expectedOutputLine "foo.custom.io/example-foo is Current: Resource is current"
|
expectedOutputLine "foo.custom.io/example-foo is Current: Resource is current"
|
||||||
|
|
||||||
kubectl get crd --no-headers | awk '{print $1}' > $OUTPUT/status
|
kubectl get crd --no-headers | awk '{print $1}' > $OUTPUT/status
|
||||||
|
|
|
||||||
|
|
@ -259,16 +259,7 @@ func (a *Applier) Run(ctx context.Context, invInfo inventory.InventoryInfo, obje
|
||||||
eventChannel <- event.Event{
|
eventChannel <- event.Event{
|
||||||
Type: event.InitType,
|
Type: event.InitType,
|
||||||
InitEvent: event.InitEvent{
|
InitEvent: event.InitEvent{
|
||||||
ResourceGroups: []event.ResourceGroup{
|
ActionGroups: taskQueue.ToActionGroups(),
|
||||||
{
|
|
||||||
Action: event.ApplyAction,
|
|
||||||
Identifiers: resourceObjects.IdsForApply(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Action: event.PruneAction,
|
|
||||||
Identifiers: resourceObjects.IdsForPrune(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,7 +267,7 @@ func (a *Applier) Run(ctx context.Context, invInfo inventory.InventoryInfo, obje
|
||||||
klog.V(4).Infoln("applier building TaskStatusRunner...")
|
klog.V(4).Infoln("applier building TaskStatusRunner...")
|
||||||
runner := taskrunner.NewTaskStatusRunner(resourceObjects.AllIds(), a.StatusPoller)
|
runner := taskrunner.NewTaskStatusRunner(resourceObjects.AllIds(), a.StatusPoller)
|
||||||
klog.V(4).Infoln("applier running TaskStatusRunner...")
|
klog.V(4).Infoln("applier running TaskStatusRunner...")
|
||||||
err = runner.Run(ctx, taskQueue, eventChannel, taskrunner.Options{
|
err = runner.Run(ctx, taskQueue.ToChannel(), eventChannel, taskrunner.Options{
|
||||||
PollInterval: options.PollInterval,
|
PollInterval: options.PollInterval,
|
||||||
UseCache: true,
|
UseCache: true,
|
||||||
EmitStatusEvents: options.EmitStatusEvents,
|
EmitStatusEvents: options.EmitStatusEvents,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -35,6 +34,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
"sigs.k8s.io/cli-utils/pkg/provider"
|
"sigs.k8s.io/cli-utils/pkg/provider"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -138,18 +138,6 @@ func (i inventoryInfo) toWrapped() inventory.InventoryInfo {
|
||||||
return inventory.WrapInventoryInfoObj(inv)
|
return inventory.WrapInventoryInfoObj(inv)
|
||||||
}
|
}
|
||||||
|
|
||||||
type expectedEvent struct {
|
|
||||||
eventType event.Type
|
|
||||||
|
|
||||||
applyEventType event.ApplyEventType
|
|
||||||
statusEventType event.StatusEventType
|
|
||||||
pruneEventType event.PruneEventType
|
|
||||||
deleteEventType event.DeleteEventType
|
|
||||||
|
|
||||||
applyErrorType error
|
|
||||||
pruneEventOp event.PruneEventOperation
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApplier(t *testing.T) {
|
func TestApplier(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
namespace string
|
namespace string
|
||||||
|
|
@ -161,7 +149,7 @@ func TestApplier(t *testing.T) {
|
||||||
prune bool
|
prune bool
|
||||||
inventoryPolicy inventory.InventoryPolicy
|
inventoryPolicy inventory.InventoryPolicy
|
||||||
statusEvents []pollevent.Event
|
statusEvents []pollevent.Event
|
||||||
expectedEventTypes []expectedEvent
|
expectedEvents []testutil.ExpEvent
|
||||||
}{
|
}{
|
||||||
"initial apply without status or prune": {
|
"initial apply without status or prune": {
|
||||||
namespace: "default",
|
namespace: "default",
|
||||||
|
|
@ -177,17 +165,18 @@ func TestApplier(t *testing.T) {
|
||||||
reconcileTimeout: time.Duration(0),
|
reconcileTimeout: time.Duration(0),
|
||||||
prune: false,
|
prune: false,
|
||||||
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -232,41 +221,42 @@ func TestApplier(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ActionGroupType,
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ActionGroupType,
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.StatusType,
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.StatusType,
|
||||||
statusEventType: event.StatusEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.StatusType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -310,37 +300,39 @@ func TestApplier(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ActionGroupType,
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ActionGroupType,
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.StatusType,
|
||||||
statusEventType: event.StatusEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.StatusType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -368,31 +360,39 @@ func TestApplier(t *testing.T) {
|
||||||
prune: true,
|
prune: true,
|
||||||
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
||||||
statusEvents: []pollevent.Event{},
|
statusEvents: []pollevent.Event{},
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ActionGroupType,
|
||||||
statusEventType: event.StatusEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventResourceUpdate,
|
|
||||||
pruneEventOp: event.Pruned,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventResourceUpdate,
|
|
||||||
pruneEventOp: event.Pruned,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.PruneType,
|
||||||
|
PruneEvent: &testutil.ExpPruneEvent{
|
||||||
|
Operation: event.Pruned,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.PruneType,
|
||||||
|
PruneEvent: &testutil.ExpPruneEvent{
|
||||||
|
Operation: event.Pruned,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -412,27 +412,49 @@ func TestApplier(t *testing.T) {
|
||||||
reconcileTimeout: time.Minute,
|
reconcileTimeout: time.Minute,
|
||||||
prune: true,
|
prune: true,
|
||||||
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
||||||
statusEvents: []pollevent.Event{},
|
statusEvents: []pollevent.Event{
|
||||||
expectedEventTypes: []expectedEvent{
|
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: pollevent.ResourceUpdateEvent,
|
||||||
|
Resource: &pollevent.ResourceStatus{
|
||||||
|
Identifier: toIdentifier(t, resources["deployment"]),
|
||||||
|
Status: status.InProgressStatus,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: pollevent.ResourceUpdateEvent,
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Resource: &pollevent.ResourceStatus{
|
||||||
applyErrorType: inventory.NewInventoryOverlapError(fmt.Errorf("")),
|
Identifier: toIdentifier(t, resources["deployment"]),
|
||||||
|
Status: status.CurrentStatus,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedEvents: []testutil.ExpEvent{
|
||||||
|
{
|
||||||
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.ApplyType,
|
||||||
statusEventType: event.StatusEventCompleted,
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
|
Error: inventory.NewInventoryOverlapError(fmt.Errorf("")),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -455,22 +477,27 @@ func TestApplier(t *testing.T) {
|
||||||
reconcileTimeout: 0,
|
reconcileTimeout: 0,
|
||||||
prune: true,
|
prune: true,
|
||||||
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventResourceUpdate,
|
|
||||||
pruneEventOp: event.PruneSkipped,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.PruneType,
|
||||||
|
PruneEvent: &testutil.ExpPruneEvent{
|
||||||
|
Operation: event.PruneSkipped,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -493,22 +520,27 @@ func TestApplier(t *testing.T) {
|
||||||
reconcileTimeout: 0,
|
reconcileTimeout: 0,
|
||||||
prune: true,
|
prune: true,
|
||||||
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
inventoryPolicy: inventory.InventoryPolicyMustMatch,
|
||||||
expectedEventTypes: []expectedEvent{
|
expectedEvents: []testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ActionGroupType,
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventResourceUpdate,
|
|
||||||
pruneEventOp: event.Pruned,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.PruneType,
|
EventType: event.ActionGroupType,
|
||||||
pruneEventType: event.PruneEventCompleted,
|
},
|
||||||
|
{
|
||||||
|
EventType: event.PruneType,
|
||||||
|
PruneEvent: &testutil.ExpPruneEvent{
|
||||||
|
Operation: event.Pruned,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -590,39 +622,29 @@ func TestApplier(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
var events []event.Event
|
var events []event.Event
|
||||||
for e := range eventChannel {
|
timer := time.NewTimer(30 * time.Second)
|
||||||
if e.Type == event.ApplyType && e.ApplyEvent.Type == event.ApplyEventCompleted {
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e, ok := <-eventChannel:
|
||||||
|
if !ok {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if e.Type == event.ActionGroupType &&
|
||||||
|
e.ActionGroupEvent.Action == event.ApplyAction &&
|
||||||
|
e.ActionGroupEvent.Type == event.Finished {
|
||||||
close(poller.start)
|
close(poller.start)
|
||||||
}
|
}
|
||||||
events = append(events, e)
|
events = append(events, e)
|
||||||
|
case <-timer.C:
|
||||||
|
t.Errorf("timeout")
|
||||||
|
break loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, len(tc.expectedEventTypes), len(events)) {
|
err = testutil.VerifyEvents(tc.expectedEvents, events)
|
||||||
t.FailNow()
|
assert.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
for i, e := range events {
|
|
||||||
expected := tc.expectedEventTypes[i]
|
|
||||||
if !assert.Equal(t, expected.eventType.String(), e.Type.String()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch expected.eventType {
|
|
||||||
case event.InitType:
|
|
||||||
case event.ApplyType:
|
|
||||||
assert.Equal(t, expected.applyEventType.String(), e.ApplyEvent.Type.String())
|
|
||||||
assert.Equal(t, reflect.TypeOf(expected.applyErrorType), reflect.TypeOf(e.ApplyEvent.Error))
|
|
||||||
case event.StatusType:
|
|
||||||
assert.Equal(t, expected.statusEventType.String(), e.StatusEvent.Type.String())
|
|
||||||
case event.PruneType:
|
|
||||||
assert.Equal(t, expected.pruneEventType.String(), e.PruneEvent.Type.String())
|
|
||||||
assert.Equal(t, expected.pruneEventOp.String(), e.PruneEvent.Operation.String())
|
|
||||||
case event.DeleteType:
|
|
||||||
assert.Equal(t, expected.deleteEventType.String(), e.DeleteEvent.Type.String())
|
|
||||||
default:
|
|
||||||
assert.Fail(t, "unexpected event type %s", expected.eventType.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,9 +110,11 @@ func (d *Destroyer) Run(inv inventory.InventoryInfo, option *DestroyerOption) <-
|
||||||
// events and shut down before we continue.
|
// events and shut down before we continue.
|
||||||
<-completedChannel
|
<-completedChannel
|
||||||
ch <- event.Event{
|
ch <- event.Event{
|
||||||
Type: event.DeleteType,
|
Type: event.ActionGroupType,
|
||||||
DeleteEvent: event.DeleteEvent{
|
ActionGroupEvent: event.ActionGroupEvent{
|
||||||
Type: event.DeleteEventCompleted,
|
GroupName: "destroyer",
|
||||||
|
Type: event.Finished,
|
||||||
|
Action: event.DeleteAction,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -135,19 +137,9 @@ func runPruneEventTransformer(eventChannel chan event.Event) (chan event.Event,
|
||||||
if msg.Type != event.PruneType {
|
if msg.Type != event.PruneType {
|
||||||
eventChannel <- msg
|
eventChannel <- msg
|
||||||
} else {
|
} else {
|
||||||
var deleteEventType event.DeleteEventType
|
|
||||||
switch msg.PruneEvent.Type {
|
|
||||||
case event.PruneEventFailed:
|
|
||||||
deleteEventType = event.DeleteEventFailed
|
|
||||||
case event.PruneEventCompleted:
|
|
||||||
deleteEventType = event.DeleteEventCompleted
|
|
||||||
case event.PruneEventResourceUpdate:
|
|
||||||
deleteEventType = event.DeleteEventResourceUpdate
|
|
||||||
}
|
|
||||||
eventChannel <- event.Event{
|
eventChannel <- event.Event{
|
||||||
Type: event.DeleteType,
|
Type: event.DeleteType,
|
||||||
DeleteEvent: event.DeleteEvent{
|
DeleteEvent: event.DeleteEvent{
|
||||||
Type: deleteEventType,
|
|
||||||
Operation: transformPruneOperation(msg.PruneEvent.Operation),
|
Operation: transformPruneOperation(msg.PruneEvent.Operation),
|
||||||
Object: msg.PruneEvent.Object,
|
Object: msg.PruneEvent.Object,
|
||||||
Identifier: msg.PruneEvent.Identifier,
|
Identifier: msg.PruneEvent.Identifier,
|
||||||
|
|
@ -161,12 +153,16 @@ func runPruneEventTransformer(eventChannel chan event.Event) (chan event.Event,
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformPruneOperation(pruneOp event.PruneEventOperation) event.DeleteEventOperation {
|
func transformPruneOperation(pruneOp event.PruneEventOperation) event.DeleteEventOperation {
|
||||||
|
var deleteOp event.DeleteEventOperation
|
||||||
switch pruneOp {
|
switch pruneOp {
|
||||||
case event.PruneSkipped:
|
case event.PruneSkipped:
|
||||||
return event.DeleteSkipped
|
deleteOp = event.DeleteSkipped
|
||||||
case event.Pruned:
|
case event.Pruned:
|
||||||
return event.Deleted
|
deleteOp = event.Deleted
|
||||||
|
case event.PruneUnspecified:
|
||||||
|
deleteOp = event.DeleteUnspecified
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unknown prune operation %s", pruneOp.String()))
|
panic(fmt.Errorf("unknown prune operation %s", pruneOp.String()))
|
||||||
}
|
}
|
||||||
|
return deleteOp
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Code generated by "stringer -type=ActionGroupEventType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package event
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Started-0]
|
||||||
|
_ = x[Finished-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ActionGroupEventType_name = "StartedFinished"
|
||||||
|
|
||||||
|
var _ActionGroupEventType_index = [...]uint8{0, 7, 15}
|
||||||
|
|
||||||
|
func (i ActionGroupEventType) String() string {
|
||||||
|
if i < 0 || i >= ActionGroupEventType(len(_ActionGroupEventType_index)-1) {
|
||||||
|
return "ActionGroupEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _ActionGroupEventType_name[_ActionGroupEventType_index[i]:_ActionGroupEventType_index[i+1]]
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=ApplyEventOperation"; DO NOT EDIT.
|
// Code generated by "stringer -type=ApplyEventOperation"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
@ -11,16 +8,16 @@ func _() {
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[ServersideApplied-0]
|
_ = x[ApplyUnspecified-0]
|
||||||
_ = x[Created-1]
|
_ = x[ServersideApplied-1]
|
||||||
_ = x[Unchanged-2]
|
_ = x[Created-2]
|
||||||
_ = x[Configured-3]
|
_ = x[Unchanged-3]
|
||||||
_ = x[Failed-4]
|
_ = x[Configured-4]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _ApplyEventOperation_name = "ServersideAppliedCreatedUnchangedConfiguredFailed"
|
const _ApplyEventOperation_name = "ApplyUnspecifiedServersideAppliedCreatedUnchangedConfigured"
|
||||||
|
|
||||||
var _ApplyEventOperation_index = [...]uint8{0, 17, 24, 33, 43, 49}
|
var _ApplyEventOperation_index = [...]uint8{0, 16, 33, 40, 49, 59}
|
||||||
|
|
||||||
func (i ApplyEventOperation) String() string {
|
func (i ApplyEventOperation) String() string {
|
||||||
if i < 0 || i >= ApplyEventOperation(len(_ApplyEventOperation_index)-1) {
|
if i < 0 || i >= ApplyEventOperation(len(_ApplyEventOperation_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=ApplyEventType"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[ApplyEventResourceUpdate-0]
|
|
||||||
_ = x[ApplyEventCompleted-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _ApplyEventType_name = "ApplyEventResourceUpdateApplyEventCompleted"
|
|
||||||
|
|
||||||
var _ApplyEventType_index = [...]uint8{0, 24, 43}
|
|
||||||
|
|
||||||
func (i ApplyEventType) String() string {
|
|
||||||
if i < 0 || i >= ApplyEventType(len(_ApplyEventType_index)-1) {
|
|
||||||
return "ApplyEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _ApplyEventType_name[_ApplyEventType_index[i]:_ApplyEventType_index[i+1]]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=DeleteEventOperation"; DO NOT EDIT.
|
// Code generated by "stringer -type=DeleteEventOperation"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
@ -11,13 +8,14 @@ func _() {
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[Deleted-0]
|
_ = x[DeleteUnspecified-0]
|
||||||
_ = x[DeleteSkipped-1]
|
_ = x[Deleted-1]
|
||||||
|
_ = x[DeleteSkipped-2]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _DeleteEventOperation_name = "DeletedDeleteSkipped"
|
const _DeleteEventOperation_name = "DeleteUnspecifiedDeletedDeleteSkipped"
|
||||||
|
|
||||||
var _DeleteEventOperation_index = [...]uint8{0, 7, 20}
|
var _DeleteEventOperation_index = [...]uint8{0, 17, 24, 37}
|
||||||
|
|
||||||
func (i DeleteEventOperation) String() string {
|
func (i DeleteEventOperation) String() string {
|
||||||
if i < 0 || i >= DeleteEventOperation(len(_DeleteEventOperation_index)-1) {
|
if i < 0 || i >= DeleteEventOperation(len(_DeleteEventOperation_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=DeleteEventType"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[DeleteEventResourceUpdate-0]
|
|
||||||
_ = x[DeleteEventCompleted-1]
|
|
||||||
_ = x[DeleteEventFailed-2]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _DeleteEventType_name = "DeleteEventResourceUpdateDeleteEventCompletedDeleteEventFailed"
|
|
||||||
|
|
||||||
var _DeleteEventType_index = [...]uint8{0, 25, 45, 62}
|
|
||||||
|
|
||||||
func (i DeleteEventType) String() string {
|
|
||||||
if i < 0 || i >= DeleteEventType(len(_DeleteEventType_index)-1) {
|
|
||||||
return "DeleteEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _DeleteEventType_name[_DeleteEventType_index[i]:_DeleteEventType_index[i+1]]
|
|
||||||
}
|
|
||||||
|
|
@ -16,6 +16,7 @@ type Type int
|
||||||
const (
|
const (
|
||||||
InitType Type = iota
|
InitType Type = iota
|
||||||
ErrorType
|
ErrorType
|
||||||
|
ActionGroupType
|
||||||
ApplyType
|
ApplyType
|
||||||
StatusType
|
StatusType
|
||||||
PruneType
|
PruneType
|
||||||
|
|
@ -37,6 +38,11 @@ type Event struct {
|
||||||
// ErrorEvent contains information about any errors encountered.
|
// ErrorEvent contains information about any errors encountered.
|
||||||
ErrorEvent ErrorEvent
|
ErrorEvent ErrorEvent
|
||||||
|
|
||||||
|
// ActionGroupEvent contains information about the progression of tasks
|
||||||
|
// to apply, prune, and destroy resources, and tasks that involves waiting
|
||||||
|
// for a set of resources to reach a specific state.
|
||||||
|
ActionGroupEvent ActionGroupEvent
|
||||||
|
|
||||||
// ApplyEvent contains information about progress pertaining to
|
// ApplyEvent contains information about progress pertaining to
|
||||||
// applying a resource to the cluster.
|
// applying a resource to the cluster.
|
||||||
ApplyEvent ApplyEvent
|
ApplyEvent ApplyEvent
|
||||||
|
|
@ -55,7 +61,7 @@ type Event struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitEvent struct {
|
type InitEvent struct {
|
||||||
ResourceGroups []ResourceGroup
|
ActionGroups []ActionGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=ResourceAction
|
//go:generate stringer -type=ResourceAction
|
||||||
|
|
@ -64,9 +70,12 @@ type ResourceAction int
|
||||||
const (
|
const (
|
||||||
ApplyAction ResourceAction = iota
|
ApplyAction ResourceAction = iota
|
||||||
PruneAction
|
PruneAction
|
||||||
|
DeleteAction
|
||||||
|
WaitAction
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResourceGroup struct {
|
type ActionGroup struct {
|
||||||
|
Name string
|
||||||
Action ResourceAction
|
Action ResourceAction
|
||||||
Identifiers []object.ObjMetadata
|
Identifiers []object.ObjMetadata
|
||||||
}
|
}
|
||||||
|
|
@ -75,92 +84,73 @@ type ErrorEvent struct {
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=ApplyEventType
|
//go:generate stringer -type=ActionGroupEventType
|
||||||
type ApplyEventType int
|
type ActionGroupEventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ApplyEventResourceUpdate ApplyEventType = iota
|
Started ActionGroupEventType = iota
|
||||||
ApplyEventCompleted
|
Finished
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ActionGroupEvent struct {
|
||||||
|
GroupName string
|
||||||
|
Action ResourceAction
|
||||||
|
Type ActionGroupEventType
|
||||||
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=ApplyEventOperation
|
//go:generate stringer -type=ApplyEventOperation
|
||||||
type ApplyEventOperation int
|
type ApplyEventOperation int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ServersideApplied ApplyEventOperation = iota
|
ApplyUnspecified ApplyEventOperation = iota
|
||||||
|
ServersideApplied
|
||||||
Created
|
Created
|
||||||
Unchanged
|
Unchanged
|
||||||
Configured
|
Configured
|
||||||
Failed
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyEvent struct {
|
type ApplyEvent struct {
|
||||||
Type ApplyEventType
|
|
||||||
Operation ApplyEventOperation
|
|
||||||
Object *unstructured.Unstructured
|
|
||||||
Identifier object.ObjMetadata
|
Identifier object.ObjMetadata
|
||||||
|
Operation ApplyEventOperation
|
||||||
|
Resource *unstructured.Unstructured
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=StatusEventType
|
|
||||||
type StatusEventType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
StatusEventResourceUpdate StatusEventType = iota
|
|
||||||
StatusEventCompleted
|
|
||||||
)
|
|
||||||
|
|
||||||
type StatusEvent struct {
|
type StatusEvent struct {
|
||||||
Type StatusEventType
|
Identifier object.ObjMetadata
|
||||||
Resource *pollevent.ResourceStatus
|
PollResourceInfo *pollevent.ResourceStatus
|
||||||
|
Resource *unstructured.Unstructured
|
||||||
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=PruneEventType
|
|
||||||
type PruneEventType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
PruneEventResourceUpdate PruneEventType = iota
|
|
||||||
PruneEventCompleted
|
|
||||||
PruneEventFailed
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate stringer -type=PruneEventOperation
|
//go:generate stringer -type=PruneEventOperation
|
||||||
type PruneEventOperation int
|
type PruneEventOperation int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Pruned PruneEventOperation = iota
|
PruneUnspecified PruneEventOperation = iota
|
||||||
|
Pruned
|
||||||
PruneSkipped
|
PruneSkipped
|
||||||
)
|
)
|
||||||
|
|
||||||
type PruneEvent struct {
|
type PruneEvent struct {
|
||||||
Type PruneEventType
|
Identifier object.ObjMetadata
|
||||||
Operation PruneEventOperation
|
Operation PruneEventOperation
|
||||||
Object *unstructured.Unstructured
|
Object *unstructured.Unstructured
|
||||||
Identifier object.ObjMetadata
|
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate stringer -type=DeleteEventType
|
|
||||||
type DeleteEventType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
DeleteEventResourceUpdate DeleteEventType = iota
|
|
||||||
DeleteEventCompleted
|
|
||||||
DeleteEventFailed
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate stringer -type=DeleteEventOperation
|
//go:generate stringer -type=DeleteEventOperation
|
||||||
type DeleteEventOperation int
|
type DeleteEventOperation int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Deleted DeleteEventOperation = iota
|
DeleteUnspecified DeleteEventOperation = iota
|
||||||
|
Deleted
|
||||||
DeleteSkipped
|
DeleteSkipped
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeleteEvent struct {
|
type DeleteEvent struct {
|
||||||
Type DeleteEventType
|
Identifier object.ObjMetadata
|
||||||
Operation DeleteEventOperation
|
Operation DeleteEventOperation
|
||||||
Object *unstructured.Unstructured
|
Object *unstructured.Unstructured
|
||||||
Identifier object.ObjMetadata
|
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=PruneEventOperation"; DO NOT EDIT.
|
// Code generated by "stringer -type=PruneEventOperation"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
@ -11,13 +8,14 @@ func _() {
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[Pruned-0]
|
_ = x[PruneUnspecified-0]
|
||||||
_ = x[PruneSkipped-1]
|
_ = x[Pruned-1]
|
||||||
|
_ = x[PruneSkipped-2]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _PruneEventOperation_name = "PrunedPruneSkipped"
|
const _PruneEventOperation_name = "PruneUnspecifiedPrunedPruneSkipped"
|
||||||
|
|
||||||
var _PruneEventOperation_index = [...]uint8{0, 6, 18}
|
var _PruneEventOperation_index = [...]uint8{0, 16, 22, 34}
|
||||||
|
|
||||||
func (i PruneEventOperation) String() string {
|
func (i PruneEventOperation) String() string {
|
||||||
if i < 0 || i >= PruneEventOperation(len(_PruneEventOperation_index)-1) {
|
if i < 0 || i >= PruneEventOperation(len(_PruneEventOperation_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=PruneEventType"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[PruneEventResourceUpdate-0]
|
|
||||||
_ = x[PruneEventCompleted-1]
|
|
||||||
_ = x[PruneEventFailed-2]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _PruneEventType_name = "PruneEventResourceUpdatePruneEventCompletedPruneEventFailed"
|
|
||||||
|
|
||||||
var _PruneEventType_index = [...]uint8{0, 24, 43, 59}
|
|
||||||
|
|
||||||
func (i PruneEventType) String() string {
|
|
||||||
if i < 0 || i >= PruneEventType(len(_PruneEventType_index)-1) {
|
|
||||||
return "PruneEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _PruneEventType_name[_PruneEventType_index[i]:_PruneEventType_index[i+1]]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=ResourceAction"; DO NOT EDIT.
|
// Code generated by "stringer -type=ResourceAction"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
@ -13,11 +10,13 @@ func _() {
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[ApplyAction-0]
|
_ = x[ApplyAction-0]
|
||||||
_ = x[PruneAction-1]
|
_ = x[PruneAction-1]
|
||||||
|
_ = x[DeleteAction-2]
|
||||||
|
_ = x[WaitAction-3]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _ResourceAction_name = "ApplyActionPruneAction"
|
const _ResourceAction_name = "ApplyActionPruneActionDeleteActionWaitAction"
|
||||||
|
|
||||||
var _ResourceAction_index = [...]uint8{0, 11, 22}
|
var _ResourceAction_index = [...]uint8{0, 11, 22, 34, 44}
|
||||||
|
|
||||||
func (i ResourceAction) String() string {
|
func (i ResourceAction) String() string {
|
||||||
if i < 0 || i >= ResourceAction(len(_ResourceAction_index)-1) {
|
if i < 0 || i >= ResourceAction(len(_ResourceAction_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=StatusEventType"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[StatusEventResourceUpdate-0]
|
|
||||||
_ = x[StatusEventCompleted-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _StatusEventType_name = "StatusEventResourceUpdateStatusEventCompleted"
|
|
||||||
|
|
||||||
var _StatusEventType_index = [...]uint8{0, 25, 45}
|
|
||||||
|
|
||||||
func (i StatusEventType) String() string {
|
|
||||||
if i < 0 || i >= StatusEventType(len(_StatusEventType_index)-1) {
|
|
||||||
return "StatusEventType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _StatusEventType_name[_StatusEventType_index[i]:_StatusEventType_index[i+1]]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=Type"; DO NOT EDIT.
|
// Code generated by "stringer -type=Type"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
@ -13,15 +10,16 @@ func _() {
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[InitType-0]
|
_ = x[InitType-0]
|
||||||
_ = x[ErrorType-1]
|
_ = x[ErrorType-1]
|
||||||
_ = x[ApplyType-2]
|
_ = x[ActionGroupType-2]
|
||||||
_ = x[StatusType-3]
|
_ = x[ApplyType-3]
|
||||||
_ = x[PruneType-4]
|
_ = x[StatusType-4]
|
||||||
_ = x[DeleteType-5]
|
_ = x[PruneType-5]
|
||||||
|
_ = x[DeleteType-6]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Type_name = "InitTypeErrorTypeApplyTypeStatusTypePruneTypeDeleteType"
|
const _Type_name = "InitTypeErrorTypeActionGroupTypeApplyTypeStatusTypePruneTypeDeleteType"
|
||||||
|
|
||||||
var _Type_index = [...]uint8{0, 8, 17, 26, 36, 45, 55}
|
var _Type_index = [...]uint8{0, 8, 17, 32, 41, 51, 60, 70}
|
||||||
|
|
||||||
func (i Type) String() string {
|
func (i Type) String() string {
|
||||||
if i < 0 || i >= Type(len(_Type_index)-1) {
|
if i < 0 || i >= Type(len(_Type_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,6 @@ func createPruneEvent(id object.ObjMetadata, obj *unstructured.Unstructured, op
|
||||||
return event.Event{
|
return event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
PruneEvent: event.PruneEvent{
|
PruneEvent: event.PruneEvent{
|
||||||
Type: event.PruneEventResourceUpdate,
|
|
||||||
Operation: op,
|
Operation: op,
|
||||||
Object: obj,
|
Object: obj,
|
||||||
Identifier: id,
|
Identifier: id,
|
||||||
|
|
@ -263,7 +262,6 @@ func createPruneFailedEvent(objMeta object.ObjMetadata, err error) event.Event {
|
||||||
return event.Event{
|
return event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
PruneEvent: event.PruneEvent{
|
PruneEvent: event.PruneEvent{
|
||||||
Type: event.PruneEventFailed,
|
|
||||||
Identifier: objMeta,
|
Identifier: objMeta,
|
||||||
Error: err,
|
Error: err,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
package solver
|
package solver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
|
@ -41,6 +42,31 @@ type TaskQueueSolver struct {
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TaskQueue struct {
|
||||||
|
tasks []taskrunner.Task
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tq *TaskQueue) ToChannel() chan taskrunner.Task {
|
||||||
|
taskQueue := make(chan taskrunner.Task, len(tq.tasks))
|
||||||
|
for _, t := range tq.tasks {
|
||||||
|
taskQueue <- t
|
||||||
|
}
|
||||||
|
return taskQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tq *TaskQueue) ToActionGroups() []event.ActionGroup {
|
||||||
|
var ags []event.ActionGroup
|
||||||
|
|
||||||
|
for _, t := range tq.tasks {
|
||||||
|
ags = append(ags, event.ActionGroup{
|
||||||
|
Name: t.Name(),
|
||||||
|
Action: t.Action(),
|
||||||
|
Identifiers: t.Identifiers(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ags
|
||||||
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ServerSideOptions common.ServerSideOptions
|
ServerSideOptions common.ServerSideOptions
|
||||||
ReconcileTimeout time.Duration
|
ReconcileTimeout time.Duration
|
||||||
|
|
@ -63,7 +89,11 @@ type resourceObjects interface {
|
||||||
// and constructs the task queue. The options parameter allows
|
// and constructs the task queue. The options parameter allows
|
||||||
// customization of how the task queue are built.
|
// customization of how the task queue are built.
|
||||||
func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
||||||
o Options) chan taskrunner.Task {
|
o Options) *TaskQueue {
|
||||||
|
var applyCounter int
|
||||||
|
var pruneCounter int
|
||||||
|
var waitCounter int
|
||||||
|
|
||||||
var tasks []taskrunner.Task
|
var tasks []taskrunner.Task
|
||||||
remainingInfos := ro.ObjsForApply()
|
remainingInfos := ro.ObjsForApply()
|
||||||
// Convert slice of previous inventory objects into a map.
|
// Convert slice of previous inventory objects into a map.
|
||||||
|
|
@ -76,6 +106,7 @@ func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
||||||
crdSplitRes, hasCRDs := splitAfterCRDs(remainingInfos)
|
crdSplitRes, hasCRDs := splitAfterCRDs(remainingInfos)
|
||||||
if hasCRDs {
|
if hasCRDs {
|
||||||
tasks = append(tasks, &task.ApplyTask{
|
tasks = append(tasks, &task.ApplyTask{
|
||||||
|
TaskName: fmt.Sprintf("apply-%d", applyCounter),
|
||||||
Objects: append(crdSplitRes.before, crdSplitRes.crds...),
|
Objects: append(crdSplitRes.before, crdSplitRes.crds...),
|
||||||
CRDs: crdSplitRes.crds,
|
CRDs: crdSplitRes.crds,
|
||||||
PrevInventory: prevInventory,
|
PrevInventory: prevInventory,
|
||||||
|
|
@ -87,21 +118,24 @@ func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
||||||
InventoryPolicy: o.InventoryPolicy,
|
InventoryPolicy: o.InventoryPolicy,
|
||||||
InvInfo: ro.Inventory(),
|
InvInfo: ro.Inventory(),
|
||||||
})
|
})
|
||||||
|
applyCounter += 1
|
||||||
if !o.DryRunStrategy.ClientOrServerDryRun() {
|
if !o.DryRunStrategy.ClientOrServerDryRun() {
|
||||||
objs := object.UnstructuredsToObjMetas(crdSplitRes.crds)
|
objs := object.UnstructuredsToObjMetas(crdSplitRes.crds)
|
||||||
tasks = append(tasks, taskrunner.NewWaitTask(
|
tasks = append(tasks, taskrunner.NewWaitTask(
|
||||||
|
fmt.Sprintf("wait-%d", waitCounter),
|
||||||
objs,
|
objs,
|
||||||
taskrunner.AllCurrent,
|
taskrunner.AllCurrent,
|
||||||
1*time.Minute),
|
1*time.Minute,
|
||||||
&task.ResetRESTMapperTask{
|
t.Mapper),
|
||||||
Mapper: t.Mapper,
|
)
|
||||||
})
|
waitCounter += 1
|
||||||
}
|
}
|
||||||
remainingInfos = crdSplitRes.after
|
remainingInfos = crdSplitRes.after
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks = append(tasks,
|
tasks = append(tasks,
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: fmt.Sprintf("apply-%d", applyCounter),
|
||||||
Objects: remainingInfos,
|
Objects: remainingInfos,
|
||||||
CRDs: crdSplitRes.crds,
|
CRDs: crdSplitRes.crds,
|
||||||
PrevInventory: prevInventory,
|
PrevInventory: prevInventory,
|
||||||
|
|
@ -113,36 +147,24 @@ func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
||||||
InventoryPolicy: o.InventoryPolicy,
|
InventoryPolicy: o.InventoryPolicy,
|
||||||
InvInfo: ro.Inventory(),
|
InvInfo: ro.Inventory(),
|
||||||
},
|
},
|
||||||
&task.SendEventTask{
|
|
||||||
Event: event.Event{
|
|
||||||
Type: event.ApplyType,
|
|
||||||
ApplyEvent: event.ApplyEvent{
|
|
||||||
Type: event.ApplyEventCompleted,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if !o.DryRunStrategy.ClientOrServerDryRun() && o.ReconcileTimeout != time.Duration(0) {
|
if !o.DryRunStrategy.ClientOrServerDryRun() && o.ReconcileTimeout != time.Duration(0) {
|
||||||
tasks = append(tasks,
|
tasks = append(tasks,
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
ro.IdsForApply(),
|
fmt.Sprintf("wait-%d", waitCounter),
|
||||||
|
object.UnstructuredsToObjMetas(remainingInfos),
|
||||||
taskrunner.AllCurrent,
|
taskrunner.AllCurrent,
|
||||||
o.ReconcileTimeout),
|
o.ReconcileTimeout,
|
||||||
&task.SendEventTask{
|
t.Mapper),
|
||||||
Event: event.Event{
|
|
||||||
Type: event.StatusType,
|
|
||||||
StatusEvent: event.StatusEvent{
|
|
||||||
Type: event.StatusEventCompleted,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
waitCounter += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Prune {
|
if o.Prune {
|
||||||
tasks = append(tasks,
|
tasks = append(tasks,
|
||||||
&task.PruneTask{
|
&task.PruneTask{
|
||||||
|
TaskName: fmt.Sprintf("prune-%d", pruneCounter),
|
||||||
Objects: ro.ObjsForApply(),
|
Objects: ro.ObjsForApply(),
|
||||||
InventoryObject: ro.Inventory(),
|
InventoryObject: ro.Inventory(),
|
||||||
PruneOptions: t.PruneOptions,
|
PruneOptions: t.PruneOptions,
|
||||||
|
|
@ -150,43 +172,23 @@ func (t *TaskQueueSolver) BuildTaskQueue(ro resourceObjects,
|
||||||
DryRunStrategy: o.DryRunStrategy,
|
DryRunStrategy: o.DryRunStrategy,
|
||||||
InventoryPolicy: o.InventoryPolicy,
|
InventoryPolicy: o.InventoryPolicy,
|
||||||
},
|
},
|
||||||
&task.SendEventTask{
|
|
||||||
Event: event.Event{
|
|
||||||
Type: event.PruneType,
|
|
||||||
PruneEvent: event.PruneEvent{
|
|
||||||
Type: event.PruneEventCompleted,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if !o.DryRunStrategy.ClientOrServerDryRun() && o.PruneTimeout != time.Duration(0) {
|
if !o.DryRunStrategy.ClientOrServerDryRun() && o.PruneTimeout != time.Duration(0) {
|
||||||
tasks = append(tasks,
|
tasks = append(tasks,
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
|
fmt.Sprintf("wait-%d", waitCounter),
|
||||||
ro.IdsForPrune(),
|
ro.IdsForPrune(),
|
||||||
taskrunner.AllNotFound,
|
taskrunner.AllNotFound,
|
||||||
o.PruneTimeout),
|
o.PruneTimeout,
|
||||||
&task.SendEventTask{
|
t.Mapper),
|
||||||
Event: event.Event{
|
|
||||||
Type: event.StatusType,
|
|
||||||
StatusEvent: event.StatusEvent{
|
|
||||||
Type: event.StatusEventCompleted,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasksToQueue(tasks)
|
return &TaskQueue{
|
||||||
}
|
tasks: tasks,
|
||||||
|
|
||||||
func tasksToQueue(tasks []taskrunner.Task) chan taskrunner.Task {
|
|
||||||
taskQueue := make(chan taskrunner.Task, len(tasks))
|
|
||||||
for _, t := range tasks {
|
|
||||||
taskQueue <- t
|
|
||||||
}
|
}
|
||||||
return taskQueue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type crdSplitResult struct {
|
type crdSplitResult struct {
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,10 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
objs: []*unstructured.Unstructured{},
|
objs: []*unstructured.Unstructured{},
|
||||||
options: Options{},
|
options: Options{},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{},
|
&task.ApplyTask{
|
||||||
&task.SendEventTask{},
|
TaskName: "apply-0",
|
||||||
|
Objects: []*unstructured.Unstructured{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"single resource": {
|
"single resource": {
|
||||||
|
|
@ -49,11 +51,11 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
options: Options{},
|
options: Options{},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"multiple resources with wait": {
|
"multiple resources with wait": {
|
||||||
|
|
@ -66,19 +68,20 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
customInfo,
|
customInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
|
"wait-0",
|
||||||
[]object.ObjMetadata{
|
[]object.ObjMetadata{
|
||||||
ignoreErrInfoToObjMeta(depInfo),
|
ignoreErrInfoToObjMeta(depInfo),
|
||||||
ignoreErrInfoToObjMeta(customInfo),
|
ignoreErrInfoToObjMeta(customInfo),
|
||||||
},
|
},
|
||||||
taskrunner.AllCurrent, 1*time.Second),
|
taskrunner.AllCurrent, 1*time.Second,
|
||||||
&task.SendEventTask{},
|
testutil.NewFakeRESTMapper()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"multiple resources with wait and prune": {
|
"multiple resources with wait and prune": {
|
||||||
|
|
@ -92,21 +95,23 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
customInfo,
|
customInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
|
"wait-0",
|
||||||
[]object.ObjMetadata{
|
[]object.ObjMetadata{
|
||||||
ignoreErrInfoToObjMeta(depInfo),
|
ignoreErrInfoToObjMeta(depInfo),
|
||||||
ignoreErrInfoToObjMeta(customInfo),
|
ignoreErrInfoToObjMeta(customInfo),
|
||||||
},
|
},
|
||||||
taskrunner.AllCurrent, 1*time.Second),
|
taskrunner.AllCurrent, 1*time.Second,
|
||||||
&task.SendEventTask{},
|
testutil.NewFakeRESTMapper()),
|
||||||
&task.PruneTask{},
|
&task.PruneTask{
|
||||||
&task.SendEventTask{},
|
TaskName: "prune-0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"multiple resources with wait, prune and dryrun": {
|
"multiple resources with wait, prune and dryrun": {
|
||||||
|
|
@ -121,14 +126,15 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
customInfo,
|
customInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
&task.PruneTask{
|
||||||
&task.PruneTask{},
|
TaskName: "prune-0",
|
||||||
&task.SendEventTask{},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"multiple resources with wait, prune and server-dryrun": {
|
"multiple resources with wait, prune and server-dryrun": {
|
||||||
|
|
@ -143,14 +149,15 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
customInfo,
|
customInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
&task.PruneTask{
|
||||||
&task.PruneTask{},
|
TaskName: "prune-0",
|
||||||
&task.SendEventTask{},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"multiple resources including CRD": {
|
"multiple resources including CRD": {
|
||||||
|
|
@ -163,29 +170,31 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
crdInfo,
|
crdInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
|
"wait-0",
|
||||||
[]object.ObjMetadata{
|
[]object.ObjMetadata{
|
||||||
ignoreErrInfoToObjMeta(crdInfo),
|
ignoreErrInfoToObjMeta(crdInfo),
|
||||||
},
|
},
|
||||||
taskrunner.AllCurrent, 1*time.Second),
|
taskrunner.AllCurrent, 1*time.Second,
|
||||||
&task.ResetRESTMapperTask{},
|
testutil.NewFakeRESTMapper()),
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-1",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
|
||||||
taskrunner.NewWaitTask(
|
taskrunner.NewWaitTask(
|
||||||
|
"wait-1",
|
||||||
[]object.ObjMetadata{
|
[]object.ObjMetadata{
|
||||||
ignoreErrInfoToObjMeta(crdInfo),
|
|
||||||
ignoreErrInfoToObjMeta(depInfo),
|
ignoreErrInfoToObjMeta(depInfo),
|
||||||
},
|
},
|
||||||
taskrunner.AllCurrent, 1*time.Second),
|
taskrunner.AllCurrent, 1*time.Second,
|
||||||
&task.SendEventTask{},
|
testutil.NewFakeRESTMapper()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"no wait with CRDs if it is a dryrun": {
|
"no wait with CRDs if it is a dryrun": {
|
||||||
|
|
@ -199,16 +208,17 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedTasks: []taskrunner.Task{
|
expectedTasks: []taskrunner.Task{
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-0",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
crdInfo,
|
crdInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.ApplyTask{
|
&task.ApplyTask{
|
||||||
|
TaskName: "apply-1",
|
||||||
Objects: []*unstructured.Unstructured{
|
Objects: []*unstructured.Unstructured{
|
||||||
depInfo,
|
depInfo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&task.SendEventTask{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -227,12 +237,11 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
idsForPrune: nil,
|
idsForPrune: nil,
|
||||||
}, tc.options)
|
}, tc.options)
|
||||||
|
|
||||||
tasks := queueToSlice(tq)
|
assert.Equal(t, len(tc.expectedTasks), len(tq.tasks))
|
||||||
|
|
||||||
assert.Equal(t, len(tc.expectedTasks), len(tasks))
|
|
||||||
for i, expTask := range tc.expectedTasks {
|
for i, expTask := range tc.expectedTasks {
|
||||||
actualTask := tasks[i]
|
actualTask := tq.tasks[i]
|
||||||
assert.Equal(t, getType(expTask), getType(actualTask))
|
assert.Equal(t, getType(expTask), getType(actualTask))
|
||||||
|
assert.Equal(t, expTask.Name(), actualTask.Name())
|
||||||
|
|
||||||
switch expTsk := expTask.(type) {
|
switch expTsk := expTask.(type) {
|
||||||
case *task.ApplyTask:
|
case *task.ApplyTask:
|
||||||
|
|
@ -244,9 +253,9 @@ func TestTaskQueueSolver_BuildTaskQueue(t *testing.T) {
|
||||||
}
|
}
|
||||||
case *taskrunner.WaitTask:
|
case *taskrunner.WaitTask:
|
||||||
actWaitTask := toWaitTask(t, actualTask)
|
actWaitTask := toWaitTask(t, actualTask)
|
||||||
assert.Equal(t, len(expTsk.Identifiers), len(actWaitTask.Identifiers))
|
assert.Equal(t, len(expTsk.Ids), len(actWaitTask.Ids))
|
||||||
for j, id := range expTsk.Identifiers {
|
for j, id := range expTsk.Ids {
|
||||||
actID := actWaitTask.Identifiers[j]
|
actID := actWaitTask.Ids[j]
|
||||||
assert.Equal(t, id, actID)
|
assert.Equal(t, id, actID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -292,18 +301,6 @@ func createInfo(apiVersion, kind, name, namespace string) *resource.Info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func queueToSlice(tq chan taskrunner.Task) []taskrunner.Task {
|
|
||||||
var tasks []taskrunner.Task
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case t := <-tq:
|
|
||||||
tasks = append(tasks, t)
|
|
||||||
default:
|
|
||||||
return tasks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getType(task taskrunner.Task) reflect.Type {
|
func getType(task taskrunner.Task) reflect.Type {
|
||||||
return reflect.TypeOf(task)
|
return reflect.TypeOf(task)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,8 @@ type applyOptions interface {
|
||||||
// ApplyTask applies the given Objects to the cluster
|
// ApplyTask applies the given Objects to the cluster
|
||||||
// by using the ApplyOptions.
|
// by using the ApplyOptions.
|
||||||
type ApplyTask struct {
|
type ApplyTask struct {
|
||||||
|
TaskName string
|
||||||
|
|
||||||
Factory util.Factory
|
Factory util.Factory
|
||||||
InfoHelper info.InfoHelper
|
InfoHelper info.InfoHelper
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
|
|
@ -66,6 +68,18 @@ var applyOptionsFactoryFunc = newApplyOptions
|
||||||
// getClusterObj gets the cluster object. Used for allow unit testing.
|
// getClusterObj gets the cluster object. Used for allow unit testing.
|
||||||
var getClusterObj = getClusterObject
|
var getClusterObj = getClusterObject
|
||||||
|
|
||||||
|
func (a *ApplyTask) Name() string {
|
||||||
|
return a.TaskName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApplyTask) Action() event.ResourceAction {
|
||||||
|
return event.ApplyAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApplyTask) Identifiers() []object.ObjMetadata {
|
||||||
|
return object.UnstructuredsToObjMetas(a.Objects)
|
||||||
|
}
|
||||||
|
|
||||||
// Start creates a new goroutine that will invoke
|
// Start creates a new goroutine that will invoke
|
||||||
// the Run function on the ApplyOptions to update
|
// the Run function on the ApplyOptions to update
|
||||||
// the cluster. It will push a TaskResult on the taskChannel
|
// the cluster. It will push a TaskResult on the taskChannel
|
||||||
|
|
@ -140,8 +154,8 @@ func (a *ApplyTask) Start(taskContext *taskrunner.TaskContext) {
|
||||||
klog.Errorf("unable to convert obj to info for %s/%s (%s)--continue",
|
klog.Errorf("unable to convert obj to info for %s/%s (%s)--continue",
|
||||||
obj.GetNamespace(), obj.GetName(), err)
|
obj.GetNamespace(), obj.GetName(), err)
|
||||||
}
|
}
|
||||||
taskContext.EventChannel() <- createApplyEvent(
|
taskContext.EventChannel() <- createApplyFailedEvent(id,
|
||||||
id, event.Failed, applyerror.NewUnknownTypeError(err))
|
applyerror.NewUnknownTypeError(err))
|
||||||
taskContext.CaptureResourceFailure(id)
|
taskContext.CaptureResourceFailure(id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -153,15 +167,13 @@ func (a *ApplyTask) Start(taskContext *taskrunner.TaskContext) {
|
||||||
klog.Errorf("error (%s) retrieving %s/%s from cluster--continue",
|
klog.Errorf("error (%s) retrieving %s/%s from cluster--continue",
|
||||||
err, info.Namespace, info.Name)
|
err, info.Namespace, info.Name)
|
||||||
}
|
}
|
||||||
op := event.Failed
|
|
||||||
if a.objInCluster(id) {
|
if a.objInCluster(id) {
|
||||||
// Object in cluster stays in the inventory.
|
// Object in cluster stays in the inventory.
|
||||||
klog.V(4).Infof("%s/%s apply retrieval failure, but in cluster--keep in inventory",
|
klog.V(4).Infof("%s/%s apply retrieval failure, but in cluster--keep in inventory",
|
||||||
info.Namespace, info.Name)
|
info.Namespace, info.Name)
|
||||||
invInfos[id] = info
|
invInfos[id] = info
|
||||||
op = event.Unchanged
|
|
||||||
}
|
}
|
||||||
taskContext.EventChannel() <- createApplyEvent(id, op, err)
|
taskContext.EventChannel() <- createApplyFailedEvent(id, err)
|
||||||
taskContext.CaptureResourceFailure(id)
|
taskContext.CaptureResourceFailure(id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -173,10 +185,12 @@ func (a *ApplyTask) Start(taskContext *taskrunner.TaskContext) {
|
||||||
if !canApply {
|
if !canApply {
|
||||||
klog.V(5).Infof("can not apply %s/%s--continue",
|
klog.V(5).Infof("can not apply %s/%s--continue",
|
||||||
clusterObj.GetNamespace(), clusterObj.GetName())
|
clusterObj.GetNamespace(), clusterObj.GetName())
|
||||||
taskContext.EventChannel() <- createApplyEvent(
|
if err != nil {
|
||||||
id,
|
taskContext.EventChannel() <- createApplyFailedEvent(id, err)
|
||||||
event.Unchanged,
|
} else {
|
||||||
err)
|
taskContext.EventChannel() <- createApplyEvent(id,
|
||||||
|
event.Unchanged, clusterObj)
|
||||||
|
}
|
||||||
taskContext.CaptureResourceFailure(id)
|
taskContext.CaptureResourceFailure(id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -202,8 +216,8 @@ func (a *ApplyTask) Start(taskContext *taskrunner.TaskContext) {
|
||||||
info.Namespace, info.Name)
|
info.Namespace, info.Name)
|
||||||
delete(invInfos, id)
|
delete(invInfos, id)
|
||||||
}
|
}
|
||||||
taskContext.EventChannel() <- createApplyEvent(
|
taskContext.EventChannel() <- createApplyFailedEvent(id,
|
||||||
id, event.Failed, applyerror.NewApplyRunError(err))
|
applyerror.NewApplyRunError(err))
|
||||||
taskContext.CaptureResourceFailure(id)
|
taskContext.CaptureResourceFailure(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -373,12 +387,21 @@ func buildCRDsInfo(crds []*unstructured.Unstructured) *crdsInfo {
|
||||||
func (a *ApplyTask) ClearTimeout() {}
|
func (a *ApplyTask) ClearTimeout() {}
|
||||||
|
|
||||||
// createApplyEvent is a helper function to package an apply event for a single resource.
|
// createApplyEvent is a helper function to package an apply event for a single resource.
|
||||||
func createApplyEvent(id object.ObjMetadata, operation event.ApplyEventOperation, err error) event.Event {
|
func createApplyEvent(id object.ObjMetadata, operation event.ApplyEventOperation, resource *unstructured.Unstructured) event.Event {
|
||||||
return event.Event{
|
return event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
ApplyEvent: event.ApplyEvent{
|
ApplyEvent: event.ApplyEvent{
|
||||||
Type: event.ApplyEventResourceUpdate,
|
Identifier: id,
|
||||||
Operation: operation,
|
Operation: operation,
|
||||||
|
Resource: resource,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createApplyFailedEvent(id object.ObjMetadata, err error) event.Event {
|
||||||
|
return event.Event{
|
||||||
|
Type: event.ApplyType,
|
||||||
|
ApplyEvent: event.ApplyEvent{
|
||||||
Identifier: id,
|
Identifier: id,
|
||||||
Error: err,
|
Error: err,
|
||||||
},
|
},
|
||||||
|
|
@ -390,8 +413,8 @@ func createApplyEvent(id object.ObjMetadata, operation event.ApplyEventOperation
|
||||||
func sendBatchApplyEvents(taskContext *taskrunner.TaskContext, objects []*unstructured.Unstructured, err error) {
|
func sendBatchApplyEvents(taskContext *taskrunner.TaskContext, objects []*unstructured.Unstructured, err error) {
|
||||||
for _, obj := range objects {
|
for _, obj := range objects {
|
||||||
id := object.UnstructuredToObjMeta(obj)
|
id := object.UnstructuredToObjMeta(obj)
|
||||||
taskContext.EventChannel() <- createApplyEvent(
|
taskContext.EventChannel() <- createApplyFailedEvent(id,
|
||||||
id, event.Failed, applyerror.NewInitializeApplyOptionError(err))
|
applyerror.NewInitializeApplyOptionError(err))
|
||||||
taskContext.CaptureResourceFailure(id)
|
taskContext.CaptureResourceFailure(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,9 @@ func (r *resourcePrinterImpl) PrintObj(obj runtime.Object, _ io.Writer) error {
|
||||||
r.ch <- event.Event{
|
r.ch <- event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
ApplyEvent: event.ApplyEvent{
|
ApplyEvent: event.ApplyEvent{
|
||||||
Type: event.ApplyEventResourceUpdate,
|
|
||||||
Operation: r.applyOperation,
|
|
||||||
Object: obj.(*unstructured.Unstructured),
|
|
||||||
Identifier: object.RuntimeToObjMeta(obj),
|
Identifier: object.RuntimeToObjMeta(obj),
|
||||||
|
Operation: r.applyOperation,
|
||||||
|
Resource: obj.(*unstructured.Unstructured),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -50,5 +50,5 @@ func TestKubectlPrinterAdapter(t *testing.T) {
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, event.ServersideApplied, msg.ApplyEvent.Operation)
|
assert.Equal(t, event.ServersideApplied, msg.ApplyEvent.Operation)
|
||||||
assert.Equal(t, deployment, msg.ApplyEvent.Object)
|
assert.Equal(t, deployment, msg.ApplyEvent.Resource)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,20 @@ package task
|
||||||
import (
|
import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/prune"
|
"sigs.k8s.io/cli-utils/pkg/apply/prune"
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
|
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
|
||||||
"sigs.k8s.io/cli-utils/pkg/common"
|
"sigs.k8s.io/cli-utils/pkg/common"
|
||||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PruneTask prunes objects from the cluster
|
// PruneTask prunes objects from the cluster
|
||||||
// by using the PruneOptions. The provided Objects is the
|
// by using the PruneOptions. The provided Objects is the
|
||||||
// set of resources that have just been applied.
|
// set of resources that have just been applied.
|
||||||
type PruneTask struct {
|
type PruneTask struct {
|
||||||
|
TaskName string
|
||||||
|
|
||||||
PruneOptions *prune.PruneOptions
|
PruneOptions *prune.PruneOptions
|
||||||
InventoryObject inventory.InventoryInfo
|
InventoryObject inventory.InventoryInfo
|
||||||
Objects []*unstructured.Unstructured
|
Objects []*unstructured.Unstructured
|
||||||
|
|
@ -24,6 +28,18 @@ type PruneTask struct {
|
||||||
InventoryPolicy inventory.InventoryPolicy
|
InventoryPolicy inventory.InventoryPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PruneTask) Name() string {
|
||||||
|
return p.TaskName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PruneTask) Action() event.ResourceAction {
|
||||||
|
return event.PruneAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PruneTask) Identifiers() []object.ObjMetadata {
|
||||||
|
return object.UnstructuredsToObjMetas(p.Objects)
|
||||||
|
}
|
||||||
|
|
||||||
// Start creates a new goroutine that will invoke
|
// Start creates a new goroutine that will invoke
|
||||||
// the Run function on the PruneOptions to update
|
// the Run function on the PruneOptions to update
|
||||||
// the cluster. It will push a TaskResult on the taskChannel
|
// the cluster. It will push a TaskResult on the taskChannel
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func getGeneration(r *event.ResourceStatus) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditionMet tests whether the provided Condition holds true for
|
// conditionMet tests whether the provided Condition holds true for
|
||||||
// all resources given by the list of Identifiers.
|
// all resources given by the list of Ids.
|
||||||
func (a *resourceStatusCollector) conditionMet(rwd []resourceWaitData, c Condition) bool {
|
func (a *resourceStatusCollector) conditionMet(rwd []resourceWaitData, c Condition) bool {
|
||||||
switch c {
|
switch c {
|
||||||
case AllCurrent:
|
case AllCurrent:
|
||||||
|
|
@ -75,7 +75,7 @@ func (a *resourceStatusCollector) conditionMet(rwd []resourceWaitData, c Conditi
|
||||||
}
|
}
|
||||||
|
|
||||||
// allMatchStatus checks whether all resources given by the
|
// allMatchStatus checks whether all resources given by the
|
||||||
// Identifiers parameter has the provided status.
|
// Ids parameter has the provided status.
|
||||||
func (a *resourceStatusCollector) allMatchStatus(rwd []resourceWaitData, s status.Status) bool {
|
func (a *resourceStatusCollector) allMatchStatus(rwd []resourceWaitData, s status.Status) bool {
|
||||||
for _, wd := range rwd {
|
for _, wd := range rwd {
|
||||||
ri, found := a.resourceMap[wd.identifier]
|
ri, found := a.resourceMap[wd.identifier]
|
||||||
|
|
@ -90,7 +90,7 @@ func (a *resourceStatusCollector) allMatchStatus(rwd []resourceWaitData, s statu
|
||||||
}
|
}
|
||||||
|
|
||||||
// noneMatchStatus checks whether none of the resources given
|
// noneMatchStatus checks whether none of the resources given
|
||||||
// by the Identifiers parameters has the provided status.
|
// by the Ids parameters has the provided status.
|
||||||
func (a *resourceStatusCollector) noneMatchStatus(rwd []resourceWaitData, s status.Status) bool {
|
func (a *resourceStatusCollector) noneMatchStatus(rwd []resourceWaitData, s status.Status) bool {
|
||||||
for _, wd := range rwd {
|
for _, wd := range rwd {
|
||||||
ri, found := a.resourceMap[wd.identifier]
|
ri, found := a.resourceMap[wd.identifier]
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ func (b *baseRunner) run(ctx context.Context, taskQueue chan Task,
|
||||||
// If the statusChannel has closed or we are preparing
|
// If the statusChannel has closed or we are preparing
|
||||||
// to abort the task processing, we just ignore all
|
// to abort the task processing, we just ignore all
|
||||||
// statusEvents.
|
// statusEvents.
|
||||||
// TODO(mortent): Check if a losed statusChannel might
|
// TODO(mortent): Check if a closed statusChannel might
|
||||||
// create a busy loop here.
|
// create a busy loop here.
|
||||||
if !ok || abort {
|
if !ok || abort {
|
||||||
continue
|
continue
|
||||||
|
|
@ -183,8 +183,10 @@ func (b *baseRunner) run(ctx context.Context, taskQueue chan Task,
|
||||||
eventChannel <- event.Event{
|
eventChannel <- event.Event{
|
||||||
Type: event.StatusType,
|
Type: event.StatusType,
|
||||||
StatusEvent: event.StatusEvent{
|
StatusEvent: event.StatusEvent{
|
||||||
Type: event.StatusEventResourceUpdate,
|
Identifier: statusEvent.Resource.Identifier,
|
||||||
Resource: statusEvent.Resource,
|
PollResourceInfo: statusEvent.Resource,
|
||||||
|
Resource: statusEvent.Resource.Resource,
|
||||||
|
Error: statusEvent.Error,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,6 +210,14 @@ func (b *baseRunner) run(ctx context.Context, taskQueue chan Task,
|
||||||
// If everything is ok, we fetch and start the next task.
|
// If everything is ok, we fetch and start the next task.
|
||||||
case msg := <-taskContext.TaskChannel():
|
case msg := <-taskContext.TaskChannel():
|
||||||
currentTask.ClearTimeout()
|
currentTask.ClearTimeout()
|
||||||
|
taskContext.EventChannel() <- event.Event{
|
||||||
|
Type: event.ActionGroupType,
|
||||||
|
ActionGroupEvent: event.ActionGroupEvent{
|
||||||
|
GroupName: currentTask.Name(),
|
||||||
|
Action: currentTask.Action(),
|
||||||
|
Type: event.Finished,
|
||||||
|
},
|
||||||
|
}
|
||||||
if msg.Err != nil {
|
if msg.Err != nil {
|
||||||
b.amendTimeoutError(msg.Err)
|
b.amendTimeoutError(msg.Err)
|
||||||
return msg.Err
|
return msg.Err
|
||||||
|
|
@ -277,6 +287,15 @@ func (b *baseRunner) nextTask(taskQueue chan Task,
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
taskContext.EventChannel() <- event.Event{
|
||||||
|
Type: event.ActionGroupType,
|
||||||
|
ActionGroupEvent: event.ActionGroupEvent{
|
||||||
|
GroupName: tsk.Name(),
|
||||||
|
Action: tsk.Action(),
|
||||||
|
Type: event.Started,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
switch st := tsk.(type) {
|
switch st := tsk.(type) {
|
||||||
case *WaitTask:
|
case *WaitTask:
|
||||||
// The wait tasks need to be handled specifically here. Before
|
// The wait tasks need to be handled specifically here. Before
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
|
pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -50,15 +51,15 @@ func TestBaseRunner(t *testing.T) {
|
||||||
"wait task runs until condition is met": {
|
"wait task runs until condition is met": {
|
||||||
identifiers: []object.ObjMetadata{depID, cmID},
|
identifiers: []object.ObjMetadata{depID, cmID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
},
|
},
|
||||||
duration: 3 * time.Second,
|
duration: 3 * time.Second,
|
||||||
},
|
},
|
||||||
NewWaitTask([]object.ObjMetadata{depID, cmID}, AllCurrent,
|
NewWaitTask("wait", []object.ObjMetadata{depID, cmID}, AllCurrent,
|
||||||
1*time.Minute),
|
1*time.Minute, testutil.NewFakeRESTMapper()),
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -83,17 +84,23 @@ func TestBaseRunner(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedEventTypes: []event.Type{
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
event.ApplyType,
|
event.ApplyType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
event.StatusType,
|
event.StatusType,
|
||||||
event.StatusType,
|
event.StatusType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
event.PruneType,
|
event.PruneType,
|
||||||
|
event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"wait task times out eventually": {
|
"wait task times out eventually": {
|
||||||
identifiers: []object.ObjMetadata{depID, cmID},
|
identifiers: []object.ObjMetadata{depID, cmID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
NewWaitTask([]object.ObjMetadata{depID, cmID}, AllCurrent,
|
NewWaitTask("wait", []object.ObjMetadata{depID, cmID}, AllCurrent,
|
||||||
2*time.Second),
|
2*time.Second, testutil.NewFakeRESTMapper()),
|
||||||
},
|
},
|
||||||
statusEventsDelay: time.Second,
|
statusEventsDelay: time.Second,
|
||||||
statusEvents: []pollevent.Event{
|
statusEvents: []pollevent.Event{
|
||||||
|
|
@ -119,25 +126,25 @@ func TestBaseRunner(t *testing.T) {
|
||||||
"tasks run in order": {
|
"tasks run in order": {
|
||||||
identifiers: []object.ObjMetadata{},
|
identifiers: []object.ObjMetadata{},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
},
|
},
|
||||||
duration: 1 * time.Second,
|
duration: 1 * time.Second,
|
||||||
},
|
},
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
duration: 1 * time.Second,
|
duration: 1 * time.Second,
|
||||||
},
|
},
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
},
|
},
|
||||||
duration: 1 * time.Second,
|
duration: 1 * time.Second,
|
||||||
},
|
},
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -147,10 +154,18 @@ func TestBaseRunner(t *testing.T) {
|
||||||
statusEventsDelay: 1 * time.Second,
|
statusEventsDelay: 1 * time.Second,
|
||||||
statusEvents: []pollevent.Event{},
|
statusEvents: []pollevent.Event{},
|
||||||
expectedEventTypes: []event.Type{
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
event.ApplyType,
|
event.ApplyType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
event.PruneType,
|
event.PruneType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
event.ApplyType,
|
event.ApplyType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
event.PruneType,
|
event.PruneType,
|
||||||
|
event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -235,13 +250,13 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
"cancellation while custom task is running": {
|
"cancellation while custom task is running": {
|
||||||
identifiers: []object.ObjMetadata{depID},
|
identifiers: []object.ObjMetadata{depID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
},
|
},
|
||||||
duration: 4 * time.Second,
|
duration: 4 * time.Second,
|
||||||
},
|
},
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -250,14 +265,17 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
},
|
},
|
||||||
contextTimeout: 2 * time.Second,
|
contextTimeout: 2 * time.Second,
|
||||||
expectedEventTypes: []event.Type{
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
event.ApplyType,
|
event.ApplyType,
|
||||||
|
event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"cancellation while wait task is running": {
|
"cancellation while wait task is running": {
|
||||||
identifiers: []object.ObjMetadata{depID},
|
identifiers: []object.ObjMetadata{depID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
NewWaitTask([]object.ObjMetadata{depID}, AllCurrent, 20*time.Second),
|
NewWaitTask("wait", []object.ObjMetadata{depID}, AllCurrent,
|
||||||
&busyTask{
|
20*time.Second, testutil.NewFakeRESTMapper()),
|
||||||
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -265,19 +283,22 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
contextTimeout: 2 * time.Second,
|
contextTimeout: 2 * time.Second,
|
||||||
expectedEventTypes: []event.Type{},
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"error while custom task is running": {
|
"error while custom task is running": {
|
||||||
identifiers: []object.ObjMetadata{depID},
|
identifiers: []object.ObjMetadata{depID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.ApplyType,
|
Type: event.ApplyType,
|
||||||
},
|
},
|
||||||
duration: 2 * time.Second,
|
duration: 2 * time.Second,
|
||||||
err: testError,
|
err: testError,
|
||||||
},
|
},
|
||||||
&busyTask{
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -287,14 +308,17 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
contextTimeout: 30 * time.Second,
|
contextTimeout: 30 * time.Second,
|
||||||
expectedError: testError,
|
expectedError: testError,
|
||||||
expectedEventTypes: []event.Type{
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
event.ApplyType,
|
event.ApplyType,
|
||||||
|
event.ActionGroupType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"error from status poller while wait task is running": {
|
"error from status poller while wait task is running": {
|
||||||
identifiers: []object.ObjMetadata{depID},
|
identifiers: []object.ObjMetadata{depID},
|
||||||
tasks: []Task{
|
tasks: []Task{
|
||||||
NewWaitTask([]object.ObjMetadata{depID}, AllCurrent, 20*time.Second),
|
NewWaitTask("wait", []object.ObjMetadata{depID}, AllCurrent,
|
||||||
&busyTask{
|
20*time.Second, testutil.NewFakeRESTMapper()),
|
||||||
|
&fakeApplyTask{
|
||||||
resultEvent: event.Event{
|
resultEvent: event.Event{
|
||||||
Type: event.PruneType,
|
Type: event.PruneType,
|
||||||
},
|
},
|
||||||
|
|
@ -310,7 +334,10 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
},
|
},
|
||||||
contextTimeout: 30 * time.Second,
|
contextTimeout: 30 * time.Second,
|
||||||
expectedError: testError,
|
expectedError: testError,
|
||||||
expectedEventTypes: []event.Type{},
|
expectedEventTypes: []event.Type{
|
||||||
|
event.ActionGroupType,
|
||||||
|
event.ActionGroupType,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,20 +406,33 @@ func TestBaseRunnerCancellation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type busyTask struct {
|
type fakeApplyTask struct {
|
||||||
|
name string
|
||||||
resultEvent event.Event
|
resultEvent event.Event
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *busyTask) Start(taskContext *TaskContext) {
|
func (f *fakeApplyTask) Name() string {
|
||||||
|
return f.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeApplyTask) Action() event.ResourceAction {
|
||||||
|
return event.ApplyAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeApplyTask) Identifiers() []object.ObjMetadata {
|
||||||
|
return []object.ObjMetadata{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeApplyTask) Start(taskContext *TaskContext) {
|
||||||
go func() {
|
go func() {
|
||||||
<-time.NewTimer(b.duration).C
|
<-time.NewTimer(f.duration).C
|
||||||
taskContext.EventChannel() <- b.resultEvent
|
taskContext.EventChannel() <- f.resultEvent
|
||||||
taskContext.TaskChannel() <- TaskResult{
|
taskContext.TaskChannel() <- TaskResult{
|
||||||
Err: b.err,
|
Err: f.err,
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *busyTask) ClearTimeout() {}
|
func (f *fakeApplyTask) ClearTimeout() {}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,15 @@
|
||||||
package taskrunner
|
package taskrunner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/client-go/restmapper"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
)
|
)
|
||||||
|
|
@ -13,22 +20,27 @@ import (
|
||||||
// Task is the interface that must be implemented by
|
// Task is the interface that must be implemented by
|
||||||
// all tasks that will be executed by the taskrunner.
|
// all tasks that will be executed by the taskrunner.
|
||||||
type Task interface {
|
type Task interface {
|
||||||
|
Name() string
|
||||||
|
Action() event.ResourceAction
|
||||||
|
Identifiers() []object.ObjMetadata
|
||||||
Start(taskContext *TaskContext)
|
Start(taskContext *TaskContext)
|
||||||
ClearTimeout()
|
ClearTimeout()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWaitTask creates a new wait task where we will wait until
|
// NewWaitTask creates a new wait task where we will wait until
|
||||||
// the resources specifies by ids all meet the specified condition.
|
// the resources specifies by ids all meet the specified condition.
|
||||||
func NewWaitTask(ids []object.ObjMetadata, cond Condition, timeout time.Duration) *WaitTask {
|
func NewWaitTask(name string, ids []object.ObjMetadata, cond Condition, timeout time.Duration, mapper meta.RESTMapper) *WaitTask {
|
||||||
// Create the token channel and only add one item.
|
// Create the token channel and only add one item.
|
||||||
tokenChannel := make(chan struct{}, 1)
|
tokenChannel := make(chan struct{}, 1)
|
||||||
tokenChannel <- struct{}{}
|
tokenChannel <- struct{}{}
|
||||||
|
|
||||||
return &WaitTask{
|
return &WaitTask{
|
||||||
Identifiers: ids,
|
name: name,
|
||||||
|
Ids: ids,
|
||||||
Condition: cond,
|
Condition: cond,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
|
|
||||||
|
mapper: mapper,
|
||||||
token: tokenChannel,
|
token: tokenChannel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,14 +53,18 @@ func NewWaitTask(ids []object.ObjMetadata, cond Condition, timeout time.Duration
|
||||||
// is handled in a special way to the taskrunner and is a part of the core
|
// is handled in a special way to the taskrunner and is a part of the core
|
||||||
// package.
|
// package.
|
||||||
type WaitTask struct {
|
type WaitTask struct {
|
||||||
// Identifiers is the list of resources that we are waiting for.
|
// name allows providing a name for the task.
|
||||||
Identifiers []object.ObjMetadata
|
name string
|
||||||
|
// Ids is the list of resources that we are waiting for.
|
||||||
|
Ids []object.ObjMetadata
|
||||||
// Condition defines the status we want all resources to reach
|
// Condition defines the status we want all resources to reach
|
||||||
Condition Condition
|
Condition Condition
|
||||||
// Timeout defines how long we are willing to wait for the condition
|
// Timeout defines how long we are willing to wait for the condition
|
||||||
// to be met.
|
// to be met.
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
||||||
|
mapper meta.RESTMapper
|
||||||
|
|
||||||
// cancelFunc is a function that will cancel the timeout timer
|
// cancelFunc is a function that will cancel the timeout timer
|
||||||
// on the task.
|
// on the task.
|
||||||
cancelFunc func()
|
cancelFunc func()
|
||||||
|
|
@ -62,6 +78,18 @@ type WaitTask struct {
|
||||||
token chan struct{}
|
token chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WaitTask) Name() string {
|
||||||
|
return w.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WaitTask) Action() event.ResourceAction {
|
||||||
|
return event.WaitAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WaitTask) Identifiers() []object.ObjMetadata {
|
||||||
|
return w.Ids
|
||||||
|
}
|
||||||
|
|
||||||
// Start kicks off the task. For the wait task, this just means
|
// Start kicks off the task. For the wait task, this just means
|
||||||
// setting up the timeout timer.
|
// setting up the timeout timer.
|
||||||
func (w *WaitTask) Start(taskContext *TaskContext) {
|
func (w *WaitTask) Start(taskContext *TaskContext) {
|
||||||
|
|
@ -84,7 +112,7 @@ func (w *WaitTask) setTimer(taskContext *TaskContext) {
|
||||||
case <-w.token:
|
case <-w.token:
|
||||||
taskContext.TaskChannel() <- TaskResult{
|
taskContext.TaskChannel() <- TaskResult{
|
||||||
Err: &TimeoutError{
|
Err: &TimeoutError{
|
||||||
Identifiers: w.Identifiers,
|
Identifiers: w.Ids,
|
||||||
Timeout: w.Timeout,
|
Timeout: w.Timeout,
|
||||||
Condition: w.Condition,
|
Condition: w.Condition,
|
||||||
},
|
},
|
||||||
|
|
@ -111,7 +139,7 @@ func (w *WaitTask) checkCondition(taskContext *TaskContext, coll *resourceStatus
|
||||||
// was applied.
|
// was applied.
|
||||||
func (w *WaitTask) computeResourceWaitData(taskContext *TaskContext) []resourceWaitData {
|
func (w *WaitTask) computeResourceWaitData(taskContext *TaskContext) []resourceWaitData {
|
||||||
var rwd []resourceWaitData
|
var rwd []resourceWaitData
|
||||||
for _, id := range w.Identifiers {
|
for _, id := range w.Ids {
|
||||||
if taskContext.ResourceFailed(id) {
|
if taskContext.ResourceFailed(id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -137,11 +165,27 @@ func (w *WaitTask) startAndComplete(taskContext *TaskContext) {
|
||||||
// for the task has been met, or something has failed so the task
|
// for the task has been met, or something has failed so the task
|
||||||
// need to be stopped.
|
// need to be stopped.
|
||||||
func (w *WaitTask) complete(taskContext *TaskContext) {
|
func (w *WaitTask) complete(taskContext *TaskContext) {
|
||||||
|
var err error
|
||||||
|
for _, obj := range w.Ids {
|
||||||
|
if (obj.GroupKind.Group == v1.SchemeGroupVersion.Group ||
|
||||||
|
obj.GroupKind.Group == v1beta1.SchemeGroupVersion.Group) &&
|
||||||
|
obj.GroupKind.Kind == "CustomResourceDefinition" {
|
||||||
|
ddRESTMapper, err := extractDeferredDiscoveryRESTMapper(w.mapper)
|
||||||
|
if err == nil {
|
||||||
|
ddRESTMapper.Reset()
|
||||||
|
// We only need to reset once.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
// Only do something if we can get the token.
|
// Only do something if we can get the token.
|
||||||
case <-w.token:
|
case <-w.token:
|
||||||
go func() {
|
go func() {
|
||||||
taskContext.TaskChannel() <- TaskResult{}
|
taskContext.TaskChannel() <- TaskResult{
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
|
|
@ -185,3 +229,20 @@ func (c Condition) Meets(s status.Status) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractDeferredDiscoveryRESTMapper unwraps the provided RESTMapper
|
||||||
|
// interface to get access to the underlying DeferredDiscoveryRESTMapper
|
||||||
|
// that can be reset.
|
||||||
|
func extractDeferredDiscoveryRESTMapper(mapper meta.RESTMapper) (*restmapper.DeferredDiscoveryRESTMapper,
|
||||||
|
error) {
|
||||||
|
val := reflect.ValueOf(mapper)
|
||||||
|
if val.Type().Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("unexpected RESTMapper type: %s", val.Type().String())
|
||||||
|
}
|
||||||
|
fv := val.FieldByName("RESTMapper")
|
||||||
|
ddRESTMapper, ok := fv.Interface().(*restmapper.DeferredDiscoveryRESTMapper)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected RESTMapper type")
|
||||||
|
}
|
||||||
|
return ddRESTMapper, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import (
|
||||||
|
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWaitTask_TimeoutTriggered(t *testing.T) {
|
func TestWaitTask_TimeoutTriggered(t *testing.T) {
|
||||||
task := NewWaitTask([]object.ObjMetadata{}, AllCurrent, 2*time.Second)
|
task := NewWaitTask("wait", []object.ObjMetadata{}, AllCurrent,
|
||||||
|
2*time.Second, testutil.NewFakeRESTMapper())
|
||||||
|
|
||||||
eventChannel := make(chan event.Event)
|
eventChannel := make(chan event.Event)
|
||||||
taskContext := NewTaskContext(eventChannel)
|
taskContext := NewTaskContext(eventChannel)
|
||||||
|
|
@ -35,7 +37,8 @@ func TestWaitTask_TimeoutTriggered(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitTask_TimeoutCancelled(t *testing.T) {
|
func TestWaitTask_TimeoutCancelled(t *testing.T) {
|
||||||
task := NewWaitTask([]object.ObjMetadata{}, AllCurrent, 2*time.Second)
|
task := NewWaitTask("wait", []object.ObjMetadata{}, AllCurrent,
|
||||||
|
2*time.Second, testutil.NewFakeRESTMapper())
|
||||||
|
|
||||||
eventChannel := make(chan event.Event)
|
eventChannel := make(chan event.Event)
|
||||||
taskContext := NewTaskContext(eventChannel)
|
taskContext := NewTaskContext(eventChannel)
|
||||||
|
|
@ -54,7 +57,8 @@ func TestWaitTask_TimeoutCancelled(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitTask_SingleTaskResult(t *testing.T) {
|
func TestWaitTask_SingleTaskResult(t *testing.T) {
|
||||||
task := NewWaitTask([]object.ObjMetadata{}, AllCurrent, 2*time.Second)
|
task := NewWaitTask("wait", []object.ObjMetadata{}, AllCurrent,
|
||||||
|
2*time.Second, testutil.NewFakeRESTMapper())
|
||||||
|
|
||||||
eventChannel := make(chan event.Event)
|
eventChannel := make(chan event.Event)
|
||||||
taskContext := NewTaskContext(eventChannel)
|
taskContext := NewTaskContext(eventChannel)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
// Copyright 2020 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Code generated by "stringer -type=EventType"; DO NOT EDIT.
|
// Code generated by "stringer -type=EventType"; DO NOT EDIT.
|
||||||
|
|
||||||
package event
|
package event
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,19 @@ type ObjMetadata struct {
|
||||||
GroupKind schema.GroupKind
|
GroupKind schema.GroupKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ObjMetas is a slice of ObjMetadata.
|
||||||
|
type ObjMetas []ObjMetadata
|
||||||
|
|
||||||
|
// Contains checks if the provided ObjMetadata exists in the ObjMetas slice.
|
||||||
|
func (oms ObjMetas) Contains(id ObjMetadata) bool {
|
||||||
|
for _, om := range oms {
|
||||||
|
if om == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// CreateObjMetadata returns a pointer to an ObjMetadata struct filled
|
// CreateObjMetadata returns a pointer to an ObjMetadata struct filled
|
||||||
// with the passed values. This function normalizes and validates the
|
// with the passed values. This function normalizes and validates the
|
||||||
// passed fields and returns an error for bad parameters.
|
// passed fields and returns an error for bad parameters.
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Formatter interface {
|
type Formatter interface {
|
||||||
FormatApplyEvent(ae event.ApplyEvent, as *ApplyStats, c Collector) error
|
FormatApplyEvent(ae event.ApplyEvent) error
|
||||||
FormatStatusEvent(se event.StatusEvent, sc Collector) error
|
FormatStatusEvent(se event.StatusEvent) error
|
||||||
FormatPruneEvent(pe event.PruneEvent, ps *PruneStats) error
|
FormatPruneEvent(pe event.PruneEvent) error
|
||||||
FormatDeleteEvent(de event.DeleteEvent, ds *DeleteStats) error
|
FormatDeleteEvent(de event.DeleteEvent) error
|
||||||
FormatErrorEvent(ee event.ErrorEvent) error
|
FormatErrorEvent(ee event.ErrorEvent) error
|
||||||
|
FormatActionGroupEvent(age event.ActionGroupEvent, ags []event.ActionGroup, as *ApplyStats, ps *PruneStats, ds *DeleteStats, c Collector) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormatterFactory func(ioStreams genericclioptions.IOStreams,
|
type FormatterFactory func(ioStreams genericclioptions.IOStreams,
|
||||||
|
|
@ -38,6 +39,7 @@ type ApplyStats struct {
|
||||||
|
|
||||||
func (a *ApplyStats) inc(op event.ApplyEventOperation) {
|
func (a *ApplyStats) inc(op event.ApplyEventOperation) {
|
||||||
switch op {
|
switch op {
|
||||||
|
case event.ApplyUnspecified:
|
||||||
case event.ServersideApplied:
|
case event.ServersideApplied:
|
||||||
a.ServersideApplied++
|
a.ServersideApplied++
|
||||||
case event.Created:
|
case event.Created:
|
||||||
|
|
@ -46,13 +48,15 @@ func (a *ApplyStats) inc(op event.ApplyEventOperation) {
|
||||||
a.Unchanged++
|
a.Unchanged++
|
||||||
case event.Configured:
|
case event.Configured:
|
||||||
a.Configured++
|
a.Configured++
|
||||||
case event.Failed:
|
|
||||||
a.Failed++
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unknown apply operation %s", op.String()))
|
panic(fmt.Errorf("unknown apply operation %s", op.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ApplyStats) incFailed() {
|
||||||
|
a.Failed++
|
||||||
|
}
|
||||||
|
|
||||||
func (a *ApplyStats) Sum() int {
|
func (a *ApplyStats) Sum() int {
|
||||||
return a.ServersideApplied + a.Configured + a.Unchanged + a.Created + a.Failed
|
return a.ServersideApplied + a.Configured + a.Unchanged + a.Created + a.Failed
|
||||||
}
|
}
|
||||||
|
|
@ -113,69 +117,89 @@ func (sc *StatusCollector) LatestStatus() map[object.ObjMetadata]event.StatusEve
|
||||||
// format on StdOut. As we support other printer implementations
|
// format on StdOut. As we support other printer implementations
|
||||||
// this should probably be an interface.
|
// this should probably be an interface.
|
||||||
// This function will block until the channel is closed.
|
// This function will block until the channel is closed.
|
||||||
|
//nolint:gocyclo
|
||||||
func (b *BaseListPrinter) Print(ch <-chan event.Event, previewStrategy common.DryRunStrategy) error {
|
func (b *BaseListPrinter) Print(ch <-chan event.Event, previewStrategy common.DryRunStrategy) error {
|
||||||
|
var actionGroups []event.ActionGroup
|
||||||
applyStats := &ApplyStats{}
|
applyStats := &ApplyStats{}
|
||||||
|
pruneStats := &PruneStats{}
|
||||||
|
deleteStats := &DeleteStats{}
|
||||||
statusCollector := &StatusCollector{
|
statusCollector := &StatusCollector{
|
||||||
latestStatus: make(map[object.ObjMetadata]event.StatusEvent),
|
latestStatus: make(map[object.ObjMetadata]event.StatusEvent),
|
||||||
}
|
}
|
||||||
printStatus := false
|
printStatus := false
|
||||||
pruneStats := &PruneStats{}
|
|
||||||
deleteStats := &DeleteStats{}
|
|
||||||
formatter := b.FormatterFactory(b.IOStreams, previewStrategy)
|
formatter := b.FormatterFactory(b.IOStreams, previewStrategy)
|
||||||
for e := range ch {
|
for e := range ch {
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
|
case event.InitType:
|
||||||
|
actionGroups = e.InitEvent.ActionGroups
|
||||||
case event.ErrorType:
|
case event.ErrorType:
|
||||||
_ = formatter.FormatErrorEvent(e.ErrorEvent)
|
_ = formatter.FormatErrorEvent(e.ErrorEvent)
|
||||||
return e.ErrorEvent.Err
|
return e.ErrorEvent.Err
|
||||||
case event.ApplyType:
|
case event.ApplyType:
|
||||||
if e.ApplyEvent.Type == event.ApplyEventResourceUpdate {
|
|
||||||
applyStats.inc(e.ApplyEvent.Operation)
|
applyStats.inc(e.ApplyEvent.Operation)
|
||||||
|
if e.ApplyEvent.Error != nil {
|
||||||
|
applyStats.incFailed()
|
||||||
}
|
}
|
||||||
if e.ApplyEvent.Type == event.ApplyEventCompleted {
|
if err := formatter.FormatApplyEvent(e.ApplyEvent); err != nil {
|
||||||
printStatus = true
|
|
||||||
}
|
|
||||||
if err := formatter.FormatApplyEvent(e.ApplyEvent, applyStats, statusCollector); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case event.StatusType:
|
case event.StatusType:
|
||||||
if se := e.StatusEvent; se.Type == event.StatusEventResourceUpdate {
|
statusCollector.updateStatus(e.StatusEvent.Identifier, e.StatusEvent)
|
||||||
statusCollector.updateStatus(e.StatusEvent.Resource.Identifier, e.StatusEvent)
|
|
||||||
if printStatus {
|
if printStatus {
|
||||||
if err := formatter.FormatStatusEvent(e.StatusEvent, statusCollector); err != nil {
|
if err := formatter.FormatStatusEvent(e.StatusEvent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case event.PruneType:
|
case event.PruneType:
|
||||||
if e.PruneEvent.Type == event.PruneEventResourceUpdate {
|
|
||||||
switch e.PruneEvent.Operation {
|
switch e.PruneEvent.Operation {
|
||||||
case event.Pruned:
|
case event.Pruned:
|
||||||
pruneStats.incPruned()
|
pruneStats.incPruned()
|
||||||
case event.PruneSkipped:
|
case event.PruneSkipped:
|
||||||
pruneStats.incSkipped()
|
pruneStats.incSkipped()
|
||||||
}
|
}
|
||||||
}
|
if e.PruneEvent.Error != nil {
|
||||||
if e.PruneEvent.Type == event.PruneEventFailed {
|
|
||||||
pruneStats.incFailed()
|
pruneStats.incFailed()
|
||||||
}
|
}
|
||||||
if err := formatter.FormatPruneEvent(e.PruneEvent, pruneStats); err != nil {
|
if err := formatter.FormatPruneEvent(e.PruneEvent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case event.DeleteType:
|
case event.DeleteType:
|
||||||
if e.DeleteEvent.Type == event.DeleteEventResourceUpdate {
|
|
||||||
switch e.DeleteEvent.Operation {
|
switch e.DeleteEvent.Operation {
|
||||||
case event.Deleted:
|
case event.Deleted:
|
||||||
deleteStats.incDeleted()
|
deleteStats.incDeleted()
|
||||||
case event.DeleteSkipped:
|
case event.DeleteSkipped:
|
||||||
deleteStats.incSkipped()
|
deleteStats.incSkipped()
|
||||||
}
|
}
|
||||||
}
|
if e.DeleteEvent.Error != nil {
|
||||||
if e.DeleteEvent.Type == event.DeleteEventFailed {
|
|
||||||
deleteStats.incFailed()
|
deleteStats.incFailed()
|
||||||
}
|
}
|
||||||
if err := formatter.FormatDeleteEvent(e.DeleteEvent, deleteStats); err != nil {
|
if err := formatter.FormatDeleteEvent(e.DeleteEvent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case event.ActionGroupType:
|
||||||
|
if err := formatter.FormatActionGroupEvent(e.ActionGroupEvent, actionGroups, applyStats,
|
||||||
|
pruneStats, deleteStats, statusCollector); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.ActionGroupEvent.Action {
|
||||||
|
case event.ApplyAction:
|
||||||
|
if e.ActionGroupEvent.Type == event.Started {
|
||||||
|
applyStats = &ApplyStats{}
|
||||||
|
}
|
||||||
|
case event.PruneAction:
|
||||||
|
if e.ActionGroupEvent.Type == event.Started {
|
||||||
|
pruneStats = &PruneStats{}
|
||||||
|
}
|
||||||
|
case event.DeleteAction:
|
||||||
|
if e.ActionGroupEvent.Type == event.Started {
|
||||||
|
deleteStats = &DeleteStats{}
|
||||||
|
}
|
||||||
|
case event.WaitAction:
|
||||||
|
if e.ActionGroupEvent.Type == event.Started {
|
||||||
|
printStatus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
failedSum := applyStats.Failed + pruneStats.Failed + deleteStats.Failed
|
failedSum := applyStats.Failed + pruneStats.Failed + deleteStats.Failed
|
||||||
|
|
@ -184,3 +208,12 @@ func (b *BaseListPrinter) Print(ch <-chan event.Event, previewStrategy common.Dr
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ActionGroupByName(name string, ags []event.ActionGroup) (event.ActionGroup, bool) {
|
||||||
|
for _, ag := range ags {
|
||||||
|
if ag.Name == name {
|
||||||
|
return ag, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event.ActionGroup{}, false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExpEvent struct {
|
||||||
|
EventType event.Type
|
||||||
|
|
||||||
|
ActionGroupEvent *ExpActionGroupEvent
|
||||||
|
ApplyEvent *ExpApplyEvent
|
||||||
|
StatusEvent *ExpStatusEvent
|
||||||
|
PruneEvent *ExpPruneEvent
|
||||||
|
DeleteEvent *ExpDeleteEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpActionGroupEvent struct {
|
||||||
|
Name string
|
||||||
|
Action event.ResourceAction
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpApplyEvent struct {
|
||||||
|
Operation event.ApplyEventOperation
|
||||||
|
Identifier object.ObjMetadata
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpStatusEvent struct {
|
||||||
|
Identifier object.ObjMetadata
|
||||||
|
Status status.Status
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpPruneEvent struct {
|
||||||
|
Operation event.PruneEventOperation
|
||||||
|
Identifier object.ObjMetadata
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpDeleteEvent struct {
|
||||||
|
Operation event.DeleteEventOperation
|
||||||
|
Identifier object.ObjMetadata
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyEvents(expEvents []ExpEvent, events []event.Event) error {
|
||||||
|
expEventIndex := 0
|
||||||
|
for i := range events {
|
||||||
|
e := events[i]
|
||||||
|
ee := expEvents[expEventIndex]
|
||||||
|
if isMatch(ee, e) {
|
||||||
|
expEventIndex += 1
|
||||||
|
if expEventIndex >= len(expEvents) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("event %s not found", expEvents[expEventIndex].EventType)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nilIdentifier = object.ObjMetadata{}
|
||||||
|
|
||||||
|
// nolint:gocyclo
|
||||||
|
// TODO(mortent): This function is pretty complex and with quite a bit of
|
||||||
|
// duplication. We should see if there is a better way to provide a flexible
|
||||||
|
// way to verify that we go the expected events.
|
||||||
|
func isMatch(ee ExpEvent, e event.Event) bool {
|
||||||
|
if ee.EventType != e.Type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:gocritic
|
||||||
|
switch e.Type {
|
||||||
|
case event.ActionGroupType:
|
||||||
|
agee := ee.ActionGroupEvent
|
||||||
|
|
||||||
|
if agee == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
age := e.ActionGroupEvent
|
||||||
|
|
||||||
|
if agee.Name != age.GroupName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if agee.Action != age.Action {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case event.ApplyType:
|
||||||
|
aee := ee.ApplyEvent
|
||||||
|
// If no more information is specified, we consider it a match.
|
||||||
|
if aee == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ae := e.ApplyEvent
|
||||||
|
|
||||||
|
if aee.Identifier != nilIdentifier {
|
||||||
|
if aee.Identifier != ae.Identifier {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if aee.Operation != ae.Operation {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if aee.Error != nil {
|
||||||
|
return ae.Error != nil
|
||||||
|
}
|
||||||
|
return ae.Error == nil
|
||||||
|
|
||||||
|
case event.StatusType:
|
||||||
|
see := ee.StatusEvent
|
||||||
|
if see == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
se := e.StatusEvent
|
||||||
|
|
||||||
|
if see.Identifier != se.Identifier {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if see.Status != se.PollResourceInfo.Status {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if see.Error != nil {
|
||||||
|
return se.Error != nil
|
||||||
|
}
|
||||||
|
return se.Error == nil
|
||||||
|
|
||||||
|
case event.PruneType:
|
||||||
|
pee := ee.PruneEvent
|
||||||
|
if pee == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
pe := e.PruneEvent
|
||||||
|
|
||||||
|
if pee.Identifier != nilIdentifier {
|
||||||
|
if pee.Identifier != pe.Identifier {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pee.Operation != pe.Operation {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pee.Error != nil {
|
||||||
|
return pe.Error != nil
|
||||||
|
}
|
||||||
|
return pe.Error == nil
|
||||||
|
|
||||||
|
case event.DeleteType:
|
||||||
|
dee := ee.DeleteEvent
|
||||||
|
if dee == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
de := e.DeleteEvent
|
||||||
|
|
||||||
|
if dee.Identifier != nilIdentifier {
|
||||||
|
if dee.Identifier != de.Identifier {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dee.Operation != de.Operation {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if dee.Error != nil {
|
||||||
|
return de.Error != nil
|
||||||
|
}
|
||||||
|
return de.Error == nil
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply"
|
"sigs.k8s.io/cli-utils/pkg/apply"
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,18 +39,12 @@ func applyAndDestroyTest(c client.Client, invConfig InventoryConfig, inventoryNa
|
||||||
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
||||||
applierEvents = append(applierEvents, e)
|
applierEvents = append(applierEvents, e)
|
||||||
}
|
}
|
||||||
err := verifyEvents([]expEvent{
|
err := testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.ApplyType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.PruneType,
|
|
||||||
},
|
},
|
||||||
}, applierEvents)
|
}, applierEvents)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
@ -63,12 +58,9 @@ func applyAndDestroyTest(c client.Client, invConfig InventoryConfig, inventoryNa
|
||||||
destroyInv := createInventoryInfo(invConfig, inventoryName, namespaceName, inventoryID)
|
destroyInv := createInventoryInfo(invConfig, inventoryName, namespaceName, inventoryID)
|
||||||
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
||||||
destroyerEvents := runCollectNoErr(destroyer.Run(destroyInv, option))
|
destroyerEvents := runCollectNoErr(destroyer.Run(destroyInv, option))
|
||||||
err = verifyEvents([]expEvent{
|
err = testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.DeleteType,
|
EventType: event.DeleteType,
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.DeleteType,
|
|
||||||
},
|
},
|
||||||
}, destroyerEvents)
|
}, destroyerEvents)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/common"
|
"sigs.k8s.io/cli-utils/pkg/common"
|
||||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -180,184 +178,3 @@ func updateReplicas(u *unstructured.Unstructured, replicas int) *unstructured.Un
|
||||||
}
|
}
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
type expEvent struct {
|
|
||||||
eventType event.Type
|
|
||||||
|
|
||||||
applyEvent *expApplyEvent
|
|
||||||
statusEvent *expStatusEvent
|
|
||||||
pruneEvent *expPruneEvent
|
|
||||||
deleteEvent *expDeleteEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
type expApplyEvent struct {
|
|
||||||
applyEventType event.ApplyEventType
|
|
||||||
operation event.ApplyEventOperation
|
|
||||||
identifier object.ObjMetadata
|
|
||||||
error error
|
|
||||||
}
|
|
||||||
|
|
||||||
type expStatusEvent struct {
|
|
||||||
statusEventType event.StatusEventType
|
|
||||||
identifier object.ObjMetadata
|
|
||||||
status status.Status
|
|
||||||
error error
|
|
||||||
}
|
|
||||||
|
|
||||||
type expPruneEvent struct {
|
|
||||||
pruneEventType event.PruneEventType
|
|
||||||
operation event.PruneEventOperation
|
|
||||||
identifier object.ObjMetadata
|
|
||||||
error error
|
|
||||||
}
|
|
||||||
|
|
||||||
type expDeleteEvent struct {
|
|
||||||
deleteEventType event.DeleteEventType
|
|
||||||
operation event.DeleteEventOperation
|
|
||||||
identifier object.ObjMetadata
|
|
||||||
error error
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyEvents(expEvents []expEvent, events []event.Event) error {
|
|
||||||
expEventIndex := 0
|
|
||||||
for i := range events {
|
|
||||||
e := events[i]
|
|
||||||
ee := expEvents[expEventIndex]
|
|
||||||
if isMatch(ee, e) {
|
|
||||||
expEventIndex += 1
|
|
||||||
if expEventIndex >= len(expEvents) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("event %s not found", expEvents[expEventIndex].eventType)
|
|
||||||
}
|
|
||||||
|
|
||||||
var nilIdentifier = object.ObjMetadata{}
|
|
||||||
|
|
||||||
// nolint:gocyclo
|
|
||||||
// TODO(mortent): This function is pretty complex and with quite a bit of
|
|
||||||
// duplication. We should see if there is a better way to provide a flexible
|
|
||||||
// way to verify that we go the expected events.
|
|
||||||
func isMatch(ee expEvent, e event.Event) bool {
|
|
||||||
if ee.eventType != e.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:gocritic
|
|
||||||
switch e.Type {
|
|
||||||
case event.ApplyType:
|
|
||||||
aee := ee.applyEvent
|
|
||||||
// If no more information is specified, we consider it a match.
|
|
||||||
if aee == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
ae := e.ApplyEvent
|
|
||||||
|
|
||||||
if aee.applyEventType != ae.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if aee.applyEventType == event.ApplyEventResourceUpdate {
|
|
||||||
if aee.identifier != nilIdentifier {
|
|
||||||
if aee.identifier != ae.Identifier {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if aee.operation != ae.Operation {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if aee.error != nil {
|
|
||||||
return ae.Error != nil
|
|
||||||
}
|
|
||||||
return ae.Error == nil
|
|
||||||
|
|
||||||
case event.StatusType:
|
|
||||||
see := ee.statusEvent
|
|
||||||
if see == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
se := e.StatusEvent
|
|
||||||
|
|
||||||
if see.statusEventType != se.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if see.statusEventType == event.StatusEventResourceUpdate {
|
|
||||||
if see.identifier != nilIdentifier {
|
|
||||||
if see.identifier != se.Resource.Identifier {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if see.status != se.Resource.Status {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if see.error != nil {
|
|
||||||
return se.Resource.Error != nil
|
|
||||||
}
|
|
||||||
return se.Resource.Error == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case event.PruneType:
|
|
||||||
pee := ee.pruneEvent
|
|
||||||
if pee == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
pe := e.PruneEvent
|
|
||||||
|
|
||||||
if pee.pruneEventType != pe.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if pee.pruneEventType == event.PruneEventResourceUpdate {
|
|
||||||
if pee.identifier != nilIdentifier {
|
|
||||||
if pee.identifier != pe.Identifier {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pee.operation != pe.Operation {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pee.error != nil {
|
|
||||||
return pe.Error != nil
|
|
||||||
}
|
|
||||||
return pe.Error == nil
|
|
||||||
|
|
||||||
case event.DeleteType:
|
|
||||||
dee := ee.deleteEvent
|
|
||||||
if dee == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
de := e.DeleteEvent
|
|
||||||
|
|
||||||
if dee.deleteEventType != de.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if dee.deleteEventType == event.DeleteEventResourceUpdate {
|
|
||||||
if dee.identifier != nilIdentifier {
|
|
||||||
if dee.identifier != de.Identifier {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dee.operation != de.Operation {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dee.error != nil {
|
|
||||||
return de.Error != nil
|
|
||||||
}
|
|
||||||
return de.Error == nil
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -36,34 +37,23 @@ func continueOnErrorTest(_ client.Client, invConfig InventoryConfig, inventoryNa
|
||||||
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
||||||
applierEvents = append(applierEvents, e)
|
applierEvents = append(applierEvents, e)
|
||||||
}
|
}
|
||||||
err := verifyEvents([]expEvent{
|
err := testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.InitType,
|
EventType: event.InitType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Identifier: object.UnstructuredToObjMeta(manifestToUnstructured(invalidCrd)),
|
||||||
operation: event.Failed,
|
Error: fmt.Errorf("failed to apply"),
|
||||||
identifier: object.UnstructuredToObjMeta(manifestToUnstructured(invalidCrd)),
|
|
||||||
error: fmt.Errorf("failed to apply"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Created,
|
||||||
operation: event.Created,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// complete
|
|
||||||
eventType: event.ApplyType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// complete
|
|
||||||
eventType: event.PruneType,
|
|
||||||
},
|
|
||||||
}, applierEvents)
|
}, applierEvents)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
|
@ -71,13 +61,12 @@ func continueOnErrorTest(_ client.Client, invConfig InventoryConfig, inventoryNa
|
||||||
destroyer := invConfig.DestroyerFactoryFunc()
|
destroyer := invConfig.DestroyerFactoryFunc()
|
||||||
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
||||||
destroyerEvents := runCollectNoErr(destroyer.Run(inv, option))
|
destroyerEvents := runCollectNoErr(destroyer.Run(inv, option))
|
||||||
err = verifyEvents([]expEvent{
|
err = testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.DeleteType,
|
EventType: event.DeleteType,
|
||||||
deleteEvent: &expDeleteEvent{
|
DeleteEvent: &testutil.ExpDeleteEvent{
|
||||||
deleteEventType: event.DeleteEventResourceUpdate,
|
Operation: event.Deleted,
|
||||||
operation: event.Deleted,
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, destroyerEvents)
|
}, destroyerEvents)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,37 +42,33 @@ func crdTest(_ client.Client, invConfig InventoryConfig, inventoryName, namespac
|
||||||
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
Expect(e.Type).NotTo(Equal(event.ErrorType))
|
||||||
applierEvents = append(applierEvents, e)
|
applierEvents = append(applierEvents, e)
|
||||||
}
|
}
|
||||||
err := verifyEvents([]expEvent{
|
err := testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Created,
|
||||||
operation: event.Created,
|
Identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
||||||
identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.StatusType,
|
EventType: event.StatusType,
|
||||||
statusEvent: &expStatusEvent{
|
StatusEvent: &testutil.ExpStatusEvent{
|
||||||
statusEventType: event.StatusEventResourceUpdate,
|
Identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
||||||
identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
Status: status.CurrentStatus,
|
||||||
status: status.CurrentStatus,
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Created,
|
||||||
operation: event.Created,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Created,
|
||||||
operation: event.Created,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, applierEvents)
|
}, applierEvents)
|
||||||
|
|
@ -81,30 +78,27 @@ func crdTest(_ client.Client, invConfig InventoryConfig, inventoryName, namespac
|
||||||
destroyer := invConfig.DestroyerFactoryFunc()
|
destroyer := invConfig.DestroyerFactoryFunc()
|
||||||
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
|
||||||
destroyerEvents := runCollectNoErr(destroyer.Run(inv, option))
|
destroyerEvents := runCollectNoErr(destroyer.Run(inv, option))
|
||||||
err = verifyEvents([]expEvent{
|
err = testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.DeleteType,
|
EventType: event.DeleteType,
|
||||||
deleteEvent: &expDeleteEvent{
|
DeleteEvent: &testutil.ExpDeleteEvent{
|
||||||
deleteEventType: event.DeleteEventResourceUpdate,
|
Operation: event.Deleted,
|
||||||
operation: event.Deleted,
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.DeleteType,
|
EventType: event.DeleteType,
|
||||||
deleteEvent: &expDeleteEvent{
|
DeleteEvent: &testutil.ExpDeleteEvent{
|
||||||
deleteEventType: event.DeleteEventResourceUpdate,
|
Operation: event.Deleted,
|
||||||
operation: event.Deleted,
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventType: event.DeleteType,
|
EventType: event.DeleteType,
|
||||||
deleteEvent: &expDeleteEvent{
|
DeleteEvent: &testutil.ExpDeleteEvent{
|
||||||
deleteEventType: event.DeleteEventResourceUpdate,
|
Operation: event.Deleted,
|
||||||
operation: event.Deleted,
|
Identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
||||||
identifier: object.UnstructuredToObjMeta(manifestToUnstructured(crd)),
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, destroyerEvents)
|
}, destroyerEvents)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||||
"sigs.k8s.io/cli-utils/pkg/object"
|
"sigs.k8s.io/cli-utils/pkg/object"
|
||||||
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -54,20 +55,12 @@ func inventoryPolicyMustMatchTest(c client.Client, invConfig InventoryConfig, na
|
||||||
}
|
}
|
||||||
|
|
||||||
By("Verify the events")
|
By("Verify the events")
|
||||||
err := verifyEvents([]expEvent{
|
err := testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
||||||
operation: event.Unchanged,
|
Error: inventory.NewInventoryOverlapError(fmt.Errorf("test")),
|
||||||
identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
|
||||||
error: inventory.NewInventoryOverlapError(fmt.Errorf("test")),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.ApplyType,
|
|
||||||
applyEvent: &expApplyEvent{
|
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, events)
|
}, events)
|
||||||
|
|
@ -111,20 +104,13 @@ func inventoryPolicyAdoptIfNoInventoryTest(c client.Client, invConfig InventoryC
|
||||||
}
|
}
|
||||||
|
|
||||||
By("Verify the events")
|
By("Verify the events")
|
||||||
err = verifyEvents([]expEvent{
|
err = testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Configured,
|
||||||
operation: event.Configured,
|
Identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
||||||
identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.ApplyType,
|
|
||||||
applyEvent: &expApplyEvent{
|
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, events)
|
}, events)
|
||||||
|
|
@ -178,20 +164,13 @@ func inventoryPolicyAdoptAllTest(c client.Client, invConfig InventoryConfig, nam
|
||||||
}
|
}
|
||||||
|
|
||||||
By("Verify the events")
|
By("Verify the events")
|
||||||
err := verifyEvents([]expEvent{
|
err := testutil.VerifyEvents([]testutil.ExpEvent{
|
||||||
{
|
{
|
||||||
eventType: event.ApplyType,
|
EventType: event.ApplyType,
|
||||||
applyEvent: &expApplyEvent{
|
ApplyEvent: &testutil.ExpApplyEvent{
|
||||||
applyEventType: event.ApplyEventResourceUpdate,
|
Operation: event.Configured,
|
||||||
operation: event.Configured,
|
Identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
||||||
identifier: object.UnstructuredToObjMeta(deploymentManifest(namespaceName)),
|
Error: nil,
|
||||||
error: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventType: event.ApplyType,
|
|
||||||
applyEvent: &expApplyEvent{
|
|
||||||
applyEventType: event.ApplyEventCompleted,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, events)
|
}, events)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue