From c30c0fdcabeaad15220d17d64263a0d2a8acb3fb Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Wed, 23 Oct 2024 13:22:23 +0545 Subject: [PATCH] golines --- Makefile | 5 + pkg/health/health.go | 8 +- pkg/health/health_argo.go | 14 +- pkg/health/health_aws.go | 4 +- pkg/health/health_cronjob.go | 22 ++- pkg/health/health_daemonset.go | 35 ++-- pkg/health/health_deployment.go | 13 +- pkg/health/health_pod.go | 35 +++- pkg/health/health_replicaset.go | 21 ++- pkg/health/health_statefulset.go | 13 +- pkg/health/health_test.go | 310 +++++++++++++++++++++++++++---- pkg/health/status.go | 8 +- pkg/health/utils.go | 10 +- pkg/lua/custom_actions_test.go | 12 +- pkg/lua/lua.go | 9 +- pkg/lua/resources.go | 31 ++-- 16 files changed, 442 insertions(+), 108 deletions(-) diff --git a/Makefile b/Makefile index a749528..abd9764 100644 --- a/Makefile +++ b/Makefile @@ -5,3 +5,8 @@ test: .PHONY: lint lint: golangci-lint run + + if command -v golines > /dev/null 2>&1; then \ + golines -m 120 -w pkg/; \ + golines -m 120 -w events/; \ + fi \ No newline at end of file diff --git a/pkg/health/health.go b/pkg/health/health.go index 4ed31a8..cd7e430 100644 --- a/pkg/health/health.go +++ b/pkg/health/health.go @@ -109,8 +109,12 @@ func GetHealthByConfigType(configType string, obj map[string]any) HealthStatus { } // GetResourceHealth returns the health of a k8s resource -func GetResourceHealth(obj *unstructured.Unstructured, healthOverride HealthOverride) (health *HealthStatus, err error) { - if obj.GetDeletionTimestamp() != nil && !obj.GetDeletionTimestamp().IsZero() && time.Since(obj.GetDeletionTimestamp().Time) > time.Hour { +func GetResourceHealth( + obj *unstructured.Unstructured, + healthOverride HealthOverride, +) (health *HealthStatus, err error) { + if obj.GetDeletionTimestamp() != nil && !obj.GetDeletionTimestamp().IsZero() && + time.Since(obj.GetDeletionTimestamp().Time) > time.Hour { terminatingFor := time.Since(obj.GetDeletionTimestamp().Time) return &HealthStatus{ Status: "TerminatingStalled", diff --git a/pkg/health/health_argo.go b/pkg/health/health_argo.go index f1f21ac..c6a24a0 100644 --- a/pkg/health/health_argo.go +++ b/pkg/health/health_argo.go @@ -36,9 +36,19 @@ func GetArgoWorkflowHealth(obj *unstructured.Unstructured) (*HealthStatus, error case "", nodePending: return &HealthStatus{Health: HealthHealthy, Status: HealthStatusProgressing, Message: wf.Status.Message}, nil case nodeRunning: - return &HealthStatus{Ready: true, Health: HealthHealthy, Status: HealthStatusProgressing, Message: wf.Status.Message}, nil + return &HealthStatus{ + Ready: true, + Health: HealthHealthy, + Status: HealthStatusProgressing, + Message: wf.Status.Message, + }, nil case nodeSucceeded: - return &HealthStatus{Ready: true, Health: HealthHealthy, Status: HealthStatusHealthy, Message: wf.Status.Message}, nil + return &HealthStatus{ + Ready: true, + Health: HealthHealthy, + Status: HealthStatusHealthy, + Message: wf.Status.Message, + }, nil case nodeFailed, nodeError: return &HealthStatus{Health: HealthUnhealthy, Status: HealthStatusDegraded, Message: wf.Status.Message}, nil } diff --git a/pkg/health/health_aws.go b/pkg/health/health_aws.go index 7efccbf..aaaf28f 100644 --- a/pkg/health/health_aws.go +++ b/pkg/health/health_aws.go @@ -20,7 +20,9 @@ const ( func GetAWSResourceHealth(resourceType, status string) (health HealthStatus) { if resourceStatuses, found := awsResourceHealthmap[resourceType]; found { if v, found := resourceStatuses[strings.ToLower(status)]; found { - v.Status = HealthStatusCode(lo.Capitalize(strings.ReplaceAll(strings.ReplaceAll(status, "-", " "), "_", " "))) + v.Status = HealthStatusCode( + lo.Capitalize(strings.ReplaceAll(strings.ReplaceAll(status, "-", " "), "_", " ")), + ) return v } } diff --git a/pkg/health/health_cronjob.go b/pkg/health/health_cronjob.go index 8ca8b6c..22b50d8 100644 --- a/pkg/health/health_cronjob.go +++ b/pkg/health/health_cronjob.go @@ -49,17 +49,23 @@ func getBatchv1CronJobHealth(job *batchv1.CronJob) (*HealthStatus, error) { if job.Status.LastSuccessfulTime.Before(job.Status.LastScheduleTime) { return &HealthStatus{ - Ready: true, // The cronjob did in fact run - Health: HealthUnhealthy, - Status: HealthStatusError, - Message: "Last run failed, last successful run was" + job.Status.LastSuccessfulTime.Format("2006-01-02 15:04:05 -0700"), + Ready: true, // The cronjob did in fact run + Health: HealthUnhealthy, + Status: HealthStatusError, + Message: "Last run failed, last successful run was" + job.Status.LastSuccessfulTime.Format( + "2006-01-02 15:04:05 -0700", + ), }, nil } return &HealthStatus{ - Ready: true, - Health: HealthHealthy, - Status: HealthStatusCompleted, - Message: fmt.Sprintf("Last run at %s in %s", job.Status.LastScheduleTime.Format("2006-01-02 15:04:05 -0700"), job.Status.LastSuccessfulTime.Sub(job.Status.LastScheduleTime.Time)), + Ready: true, + Health: HealthHealthy, + Status: HealthStatusCompleted, + Message: fmt.Sprintf( + "Last run at %s in %s", + job.Status.LastScheduleTime.Format("2006-01-02 15:04:05 -0700"), + job.Status.LastSuccessfulTime.Sub(job.Status.LastScheduleTime.Time), + ), }, nil } diff --git a/pkg/health/health_daemonset.go b/pkg/health/health_daemonset.go index 65d7f6b..ed0ccc8 100644 --- a/pkg/health/health_daemonset.go +++ b/pkg/health/health_daemonset.go @@ -35,7 +35,8 @@ func getAppsv1DaemonSetHealth(daemon *appsv1.DaemonSet) (*HealthStatus, error) { health = HealthUnhealthy } - if daemon.Generation == daemon.Status.ObservedGeneration && daemon.Status.UpdatedNumberScheduled == daemon.Status.DesiredNumberScheduled { + if daemon.Generation == daemon.Status.ObservedGeneration && + daemon.Status.UpdatedNumberScheduled == daemon.Status.DesiredNumberScheduled { return &HealthStatus{ Health: HealthHealthy, Ready: true, @@ -45,24 +46,36 @@ func getAppsv1DaemonSetHealth(daemon *appsv1.DaemonSet) (*HealthStatus, error) { if daemon.Spec.UpdateStrategy.Type == appsv1.OnDeleteDaemonSetStrategyType { return &HealthStatus{ - Health: health, - Ready: daemon.Status.NumberAvailable == daemon.Status.DesiredNumberScheduled, - Status: HealthStatusRunning, - Message: fmt.Sprintf("%d of %d pods updated", daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), + Health: health, + Ready: daemon.Status.NumberAvailable == daemon.Status.DesiredNumberScheduled, + Status: HealthStatusRunning, + Message: fmt.Sprintf( + "%d of %d pods updated", + daemon.Status.UpdatedNumberScheduled, + daemon.Status.DesiredNumberScheduled, + ), }, nil } if daemon.Status.UpdatedNumberScheduled < daemon.Status.DesiredNumberScheduled { return &HealthStatus{ - Health: health, - Status: HealthStatusRollingOut, - Message: fmt.Sprintf("%d of %d pods updated", daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), + Health: health, + Status: HealthStatusRollingOut, + Message: fmt.Sprintf( + "%d of %d pods updated", + daemon.Status.UpdatedNumberScheduled, + daemon.Status.DesiredNumberScheduled, + ), }, nil } if daemon.Status.NumberAvailable < daemon.Status.DesiredNumberScheduled { return &HealthStatus{ - Health: health, - Status: HealthStatusRollingOut, - Message: fmt.Sprintf("%d of %d pods ready", daemon.Status.NumberAvailable, daemon.Status.DesiredNumberScheduled), + Health: health, + Status: HealthStatusRollingOut, + Message: fmt.Sprintf( + "%d of %d pods ready", + daemon.Status.NumberAvailable, + daemon.Status.DesiredNumberScheduled, + ), }, nil } diff --git a/pkg/health/health_deployment.go b/pkg/health/health_deployment.go index ea88eef..4579f3d 100644 --- a/pkg/health/health_deployment.go +++ b/pkg/health/health_deployment.go @@ -29,7 +29,9 @@ func getAppsv1DeploymentHealth(deployment *appsv1.Deployment, obj *unstructured. var containersWaitingForReadiness []string for _, container := range deployment.Spec.Template.Spec.Containers { if container.ReadinessProbe != nil && container.ReadinessProbe.InitialDelaySeconds > 0 { - deadline := deployment.CreationTimestamp.Add(time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds)) + deadline := deployment.CreationTimestamp.Add( + time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds), + ) if time.Now().Before(deadline) { containersWaitingForReadiness = append(containersWaitingForReadiness, container.Name) } @@ -38,9 +40,12 @@ func getAppsv1DeploymentHealth(deployment *appsv1.Deployment, obj *unstructured. if len(containersWaitingForReadiness) > 0 { return &HealthStatus{ - Health: HealthUnknown, - Status: HealthStatusStarting, - Message: fmt.Sprintf("Container(s) %s is waiting for readiness probe", strings.Join(containersWaitingForReadiness, ",")), + Health: HealthUnknown, + Status: HealthStatusStarting, + Message: fmt.Sprintf( + "Container(s) %s is waiting for readiness probe", + strings.Join(containersWaitingForReadiness, ","), + ), }, nil } diff --git a/pkg/health/health_pod.go b/pkg/health/health_pod.go index f6868e0..db12959 100644 --- a/pkg/health/health_pod.go +++ b/pkg/health/health_pod.go @@ -58,7 +58,8 @@ func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { getCommonContainerError := func(containerStatus *corev1.ContainerStatus) *HealthStatus { waiting := containerStatus.State.Waiting // Article listing common container errors: https://medium.com/kokster/debugging-crashloopbackoffs-with-init-containers-26f79e9fb5bf - if waiting != nil && (strings.HasPrefix(waiting.Reason, "Err") || strings.HasSuffix(waiting.Reason, "Error") || strings.HasSuffix(waiting.Reason, "BackOff")) { + if waiting != nil && + (strings.HasPrefix(waiting.Reason, "Err") || strings.HasSuffix(waiting.Reason, "Error") || strings.HasSuffix(waiting.Reason, "BackOff")) { return &HealthStatus{ Status: HealthStatusCode(waiting.Reason), Health: HealthUnhealthy, @@ -115,7 +116,8 @@ func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { switch pod.Status.Phase { case corev1.PodPending: for _, ctrStatus := range pod.Status.InitContainerStatuses { - if ctrStatus.LastTerminationState.Terminated != nil && ctrStatus.LastTerminationState.Terminated.Reason == "Error" { + if ctrStatus.LastTerminationState.Terminated != nil && + ctrStatus.LastTerminationState.Terminated.Reason == "Error" { // A pending pod whose container was previously terminated with error should be marked as unhealthy (instead of unknown) return &HealthStatus{ Health: HealthUnhealthy, @@ -156,7 +158,12 @@ func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { case corev1.PodFailed: if pod.Status.Message != "" { // Pod has a nice error message. Use that. - return &HealthStatus{Health: HealthUnhealthy, Status: HealthStatusError, Ready: true, Message: pod.Status.Message}, nil + return &HealthStatus{ + Health: HealthUnhealthy, + Status: HealthStatusError, + Ready: true, + Message: pod.Status.Message, + }, nil } for _, ctr := range append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) { if msg := getFailMessage(&ctr); msg != "" { @@ -186,7 +193,11 @@ func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { if possiblyInRestartLoop { lastTerminatedTime := s.LastTerminationState.Terminated.FinishedAt.Time - h.Message = fmt.Sprintf("%s has restarted %d time(s)", s.Name, pod.Status.ContainerStatuses[0].RestartCount) + h.Message = fmt.Sprintf( + "%s has restarted %d time(s)", + s.Name, + pod.Status.ContainerStatuses[0].RestartCount, + ) if s.LastTerminationState.Terminated.Reason != "Completed" { h.Status = HealthStatusCode(s.LastTerminationState.Terminated.Reason) @@ -243,16 +254,24 @@ func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { continue } - if c.Status.State.Running != nil && time.Since(c.Status.State.Running.StartedAt.Time) <= time.Duration(c.Spec.ReadinessProbe.InitialDelaySeconds)*time.Second { + if c.Status.State.Running != nil && + time.Since( + c.Status.State.Running.StartedAt.Time, + ) <= time.Duration( + c.Spec.ReadinessProbe.InitialDelaySeconds, + )*time.Second { containersWaitingForReadinessProbe = append(containersWaitingForReadinessProbe, c.Spec.Name) } } // otherwise we are progressing towards a ready state return &HealthStatus{ - Health: HealthUnknown, - Status: HealthStatusStarting, - Message: fmt.Sprintf("Container %s is waiting for readiness probe", strings.Join(containersWaitingForReadinessProbe, ",")), + Health: HealthUnknown, + Status: HealthStatusStarting, + Message: fmt.Sprintf( + "Container %s is waiting for readiness probe", + strings.Join(containersWaitingForReadinessProbe, ","), + ), }, nil case corev1.RestartPolicyOnFailure, corev1.RestartPolicyNever: diff --git a/pkg/health/health_replicaset.go b/pkg/health/health_replicaset.go index bc0591b..c219d1a 100644 --- a/pkg/health/health_replicaset.go +++ b/pkg/health/health_replicaset.go @@ -36,7 +36,9 @@ func getAppsv1ReplicaSetHealth(replicaSet *appsv1.ReplicaSet) (*HealthStatus, er var containersWaitingForReadiness []string for _, container := range replicaSet.Spec.Template.Spec.Containers { if container.ReadinessProbe != nil && container.ReadinessProbe.InitialDelaySeconds > 0 { - deadline := replicaSet.CreationTimestamp.Add(time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds)) + deadline := replicaSet.CreationTimestamp.Add( + time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds), + ) if time.Now().Before(deadline) { containersWaitingForReadiness = append(containersWaitingForReadiness, container.Name) } @@ -45,9 +47,12 @@ func getAppsv1ReplicaSetHealth(replicaSet *appsv1.ReplicaSet) (*HealthStatus, er if len(containersWaitingForReadiness) > 0 { return &HealthStatus{ - Health: HealthUnknown, - Status: HealthStatusStarting, - Message: fmt.Sprintf("Container(s) %s is waiting for readiness probe", strings.Join(containersWaitingForReadiness, ",")), + Health: HealthUnknown, + Status: HealthStatusStarting, + Message: fmt.Sprintf( + "Container(s) %s is waiting for readiness probe", + strings.Join(containersWaitingForReadiness, ","), + ), }, nil } @@ -73,7 +78,8 @@ func getAppsv1ReplicaSetHealth(replicaSet *appsv1.ReplicaSet) (*HealthStatus, er health = HealthUnknown } - if replicaSet.Generation == replicaSet.Status.ObservedGeneration && replicaSet.Status.ReadyReplicas == *replicaSet.Spec.Replicas { + if replicaSet.Generation == replicaSet.Status.ObservedGeneration && + replicaSet.Status.ReadyReplicas == *replicaSet.Spec.Replicas { return &HealthStatus{ Health: health, Status: HealthStatusRunning, @@ -112,7 +118,10 @@ func getAppsv1ReplicaSetHealth(replicaSet *appsv1.ReplicaSet) (*HealthStatus, er }, nil } -func getAppsv1ReplicaSetCondition(status appsv1.ReplicaSetStatus, condType appsv1.ReplicaSetConditionType) *appsv1.ReplicaSetCondition { +func getAppsv1ReplicaSetCondition( + status appsv1.ReplicaSetStatus, + condType appsv1.ReplicaSetConditionType, +) *appsv1.ReplicaSetCondition { for i := range status.Conditions { c := status.Conditions[i] if c.Type == condType { diff --git a/pkg/health/health_statefulset.go b/pkg/health/health_statefulset.go index c3c613d..de136ee 100644 --- a/pkg/health/health_statefulset.go +++ b/pkg/health/health_statefulset.go @@ -41,7 +41,9 @@ func getAppsv1StatefulSetHealth(sts *appsv1.StatefulSet) (*HealthStatus, error) var containersWaitingForReadiness []string for _, container := range sts.Spec.Template.Spec.Containers { if container.ReadinessProbe != nil && container.ReadinessProbe.InitialDelaySeconds > 0 { - deadline := sts.CreationTimestamp.Add(time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds)) + deadline := sts.CreationTimestamp.Add( + time.Second * time.Duration(container.ReadinessProbe.InitialDelaySeconds), + ) if time.Now().Before(deadline) { containersWaitingForReadiness = append(containersWaitingForReadiness, container.Name) } @@ -50,9 +52,12 @@ func getAppsv1StatefulSetHealth(sts *appsv1.StatefulSet) (*HealthStatus, error) if len(containersWaitingForReadiness) > 0 { return &HealthStatus{ - Health: HealthUnknown, - Status: HealthStatusStarting, - Message: fmt.Sprintf("Container(s) %s is waiting for readiness probe", strings.Join(containersWaitingForReadiness, ",")), + Health: HealthUnknown, + Status: HealthStatusStarting, + Message: fmt.Sprintf( + "Container(s) %s is waiting for readiness probe", + strings.Join(containersWaitingForReadiness, ","), + ), }, nil } diff --git a/pkg/health/health_test.go b/pkg/health/health_test.go index a9e4dbb..d548972 100644 --- a/pkg/health/health_test.go +++ b/pkg/health/health_test.go @@ -18,7 +18,14 @@ import ( "sigs.k8s.io/yaml" ) -func assertAppHealthMsg(t *testing.T, yamlPath string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool, expectedMsg string) { +func assertAppHealthMsg( + t *testing.T, + yamlPath string, + expectedStatus health.HealthStatusCode, + expectedHealth health.Health, + expectedReady bool, + expectedMsg string, +) { health := getHealthStatus(yamlPath, t, nil) assert.NotNil(t, health) assert.Equal(t, expectedHealth, health.Health) @@ -27,7 +34,13 @@ func assertAppHealthMsg(t *testing.T, yamlPath string, expectedStatus health.Hea assert.Equal(t, expectedMsg, health.Message) } -func assertAppHealth(t *testing.T, yamlPath string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool) { +func assertAppHealth( + t *testing.T, + yamlPath string, + expectedStatus health.HealthStatusCode, + expectedHealth health.Health, + expectedReady bool, +) { health := getHealthStatus(yamlPath, t, nil) assert.NotNil(t, health) assert.Equal(t, expectedHealth, health.Health) @@ -35,7 +48,15 @@ func assertAppHealth(t *testing.T, yamlPath string, expectedStatus health.Health assert.Equal(t, expectedStatus, health.Status) } -func assertAppHealthWithOverwriteMsg(t *testing.T, yamlPath string, overwrites map[string]string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool, expectedMsg string) { +func assertAppHealthWithOverwriteMsg( + t *testing.T, + yamlPath string, + overwrites map[string]string, + expectedStatus health.HealthStatusCode, + expectedHealth health.Health, + expectedReady bool, + expectedMsg string, +) { health := getHealthStatus(yamlPath, t, overwrites) assert.NotNil(t, health) assert.Equal(t, expectedHealth, health.Health) @@ -44,7 +65,14 @@ func assertAppHealthWithOverwriteMsg(t *testing.T, yamlPath string, overwrites m assert.Equal(t, expectedMsg, health.Message) } -func assertAppHealthWithOverwrite(t *testing.T, yamlPath string, overwrites map[string]string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool) { +func assertAppHealthWithOverwrite( + t *testing.T, + yamlPath string, + overwrites map[string]string, + expectedStatus health.HealthStatusCode, + expectedHealth health.Health, + expectedReady bool, +) { health := getHealthStatus(yamlPath, t, overwrites) assert.NotNil(t, health) assert.Equal(t, expectedHealth, health.Health) @@ -71,16 +99,50 @@ func getHealthStatus(yamlPath string, t *testing.T, overwrites map[string]string } func TestCrossplane(t *testing.T) { - assertAppHealthMsg(t, "./testdata/crossplane-apply-failure.yaml", "ApplyFailure", health.HealthWarning, true, "apply failed: an existing `high_availability.0.standby_availability_zone` can only be changed when exchanged with the zone specified in `zone`: ") + assertAppHealthMsg( + t, + "./testdata/crossplane-apply-failure.yaml", + "ApplyFailure", + health.HealthWarning, + true, + "apply failed: an existing `high_availability.0.standby_availability_zone` can only be changed when exchanged with the zone specified in `zone`: ", + ) assertAppHealthMsg(t, "./testdata/crossplane-healthy.yaml", "ReconcileSuccess", health.HealthHealthy, true, "") - assertAppHealthMsg(t, "./testdata/crossplane-installed.yaml", "ActivePackageRevision", health.HealthHealthy, true, "") - assertAppHealthMsg(t, "./testdata/crossplane-provider-revision.yaml", "HealthyPackageRevision", health.HealthHealthy, true, "") - assertAppHealthMsg(t, "./testdata/crossplane-reconcile-error.yaml", "ReconcileError", health.HealthWarning, true, "observe failed: cannot run plan: plan failed: Instance cannot be destroyed: Resource azurerm_kubernetes_cluster_node_pool.prodeu01 has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.") + assertAppHealthMsg( + t, + "./testdata/crossplane-installed.yaml", + "ActivePackageRevision", + health.HealthHealthy, + true, + "", + ) + assertAppHealthMsg( + t, + "./testdata/crossplane-provider-revision.yaml", + "HealthyPackageRevision", + health.HealthHealthy, + true, + "", + ) + assertAppHealthMsg( + t, + "./testdata/crossplane-reconcile-error.yaml", + "ReconcileError", + health.HealthWarning, + true, + "observe failed: cannot run plan: plan failed: Instance cannot be destroyed: Resource azurerm_kubernetes_cluster_node_pool.prodeu01 has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.", + ) } func TestNamespace(t *testing.T) { assertAppHealth(t, "./testdata/namespace.yaml", health.HealthStatusHealthy, health.HealthUnknown, true) - assertAppHealth(t, "./testdata/namespace-terminating.yaml", health.HealthStatusTerminating, health.HealthUnknown, false) + assertAppHealth( + t, + "./testdata/namespace-terminating.yaml", + health.HealthStatusTerminating, + health.HealthUnknown, + false, + ) } func TestCertificate(t *testing.T) { @@ -105,11 +167,35 @@ func TestExternalSecrets(t *testing.T) { func TestDeploymentHealth(t *testing.T) { assertAppHealth(t, "./testdata/nginx.yaml", health.HealthStatusRunning, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/deployment-progressing.yaml", health.HealthStatusStarting, health.HealthHealthy, false) - assertAppHealth(t, "./testdata/deployment-suspended.yaml", health.HealthStatusSuspended, health.HealthHealthy, false) + assertAppHealth( + t, + "./testdata/deployment-progressing.yaml", + health.HealthStatusStarting, + health.HealthHealthy, + false, + ) + assertAppHealth( + t, + "./testdata/deployment-suspended.yaml", + health.HealthStatusSuspended, + health.HealthHealthy, + false, + ) assertAppHealth(t, "./testdata/deployment-degraded.yaml", health.HealthStatusStarting, health.HealthHealthy, false) - assertAppHealth(t, "./testdata/deployment-scaling-down.yaml", health.HealthStatusScalingDown, health.HealthHealthy, false) - assertAppHealth(t, "./testdata/deployment-failed.yaml", health.HealthStatusRolloutFailed, health.HealthUnhealthy, false) + assertAppHealth( + t, + "./testdata/deployment-scaling-down.yaml", + health.HealthStatusScalingDown, + health.HealthHealthy, + false, + ) + assertAppHealth( + t, + "./testdata/deployment-failed.yaml", + health.HealthStatusRolloutFailed, + health.HealthUnhealthy, + false, + ) } func TestStatefulSetHealth(t *testing.T) { @@ -117,7 +203,13 @@ func TestStatefulSetHealth(t *testing.T) { } func TestStatefulSetOnDeleteHealth(t *testing.T) { - assertAppHealth(t, "./testdata/statefulset-ondelete.yaml", health.HealthStatusRollingOut, health.HealthWarning, false) + assertAppHealth( + t, + "./testdata/statefulset-ondelete.yaml", + health.HealthStatusRollingOut, + health.HealthWarning, + false, + ) } func TestDaemonSetOnDeleteHealth(t *testing.T) { @@ -131,8 +223,20 @@ func TestPVCHealth(t *testing.T) { func TestServiceHealth(t *testing.T) { assertAppHealth(t, "./testdata/svc-clusterip.yaml", health.HealthStatusUnknown, health.HealthUnknown, true) assertAppHealth(t, "./testdata/svc-loadbalancer.yaml", health.HealthStatusRunning, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/svc-loadbalancer-unassigned.yaml", health.HealthStatusCreating, health.HealthUnknown, false) - assertAppHealth(t, "./testdata/svc-loadbalancer-nonemptylist.yaml", health.HealthStatusRunning, health.HealthHealthy, true) + assertAppHealth( + t, + "./testdata/svc-loadbalancer-unassigned.yaml", + health.HealthStatusCreating, + health.HealthUnknown, + false, + ) + assertAppHealth( + t, + "./testdata/svc-loadbalancer-nonemptylist.yaml", + health.HealthStatusRunning, + health.HealthHealthy, + true, + ) } func TestIngressHealth(t *testing.T) { @@ -155,17 +259,41 @@ func TestJob(t *testing.T) { func TestHPA(t *testing.T) { assertAppHealth(t, "./testdata/hpa-v2-healthy.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) assertAppHealth(t, "./testdata/hpa-v2-degraded.yaml", health.HealthStatusDegraded, health.HealthUnhealthy, false) - assertAppHealth(t, "./testdata/hpa-v2-progressing.yaml", health.HealthStatusProgressing, health.HealthHealthy, false) + assertAppHealth( + t, + "./testdata/hpa-v2-progressing.yaml", + health.HealthStatusProgressing, + health.HealthHealthy, + false, + ) assertAppHealth(t, "./testdata/hpa-v2beta2-healthy.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/hpa-v2beta1-healthy-disabled.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) + assertAppHealth( + t, + "./testdata/hpa-v2beta1-healthy-disabled.yaml", + health.HealthStatusHealthy, + health.HealthHealthy, + true, + ) assertAppHealth(t, "./testdata/hpa-v2beta1-healthy.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) assertAppHealth(t, "./testdata/hpa-v1-degraded.yaml", health.HealthStatusDegraded, health.HealthUnhealthy, false) assertAppHealth(t, "./testdata/hpa-v2-degraded.yaml", health.HealthStatusDegraded, health.HealthUnhealthy, false) assertAppHealth(t, "./testdata/hpa-v1-healthy.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) assertAppHealth(t, "./testdata/hpa-v1-healthy-toofew.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/hpa-v1-progressing.yaml", health.HealthStatusProgressing, health.HealthHealthy, false) - assertAppHealth(t, "./testdata/hpa-v1-progressing-with-no-annotations.yaml", health.HealthStatusProgressing, health.HealthHealthy, false) + assertAppHealth( + t, + "./testdata/hpa-v1-progressing.yaml", + health.HealthStatusProgressing, + health.HealthHealthy, + false, + ) + assertAppHealth( + t, + "./testdata/hpa-v1-progressing-with-no-annotations.yaml", + health.HealthStatusProgressing, + health.HealthHealthy, + false, + ) } func TestReplicaSet(t *testing.T) { @@ -196,13 +324,29 @@ func TestPod(t *testing.T) { }, health.HealthStatusStarting, health.HealthUnknown, false, "Container nginx is waiting for readiness probe") // Pod not ready - assertAppHealth(t, "./testdata/pod-not-ready-but-container-ready.yaml", health.HealthStatusRunning, health.HealthWarning, false) + assertAppHealth( + t, + "./testdata/pod-not-ready-but-container-ready.yaml", + health.HealthStatusRunning, + health.HealthWarning, + false, + ) // Restart Loop - assertAppHealth(t, "./testdata/pod-ready-container-terminated.yaml", health.HealthStatusRunning, health.HealthHealthy, true) + assertAppHealth( + t, + "./testdata/pod-ready-container-terminated.yaml", + health.HealthStatusRunning, + health.HealthHealthy, + true, + ) assertAppHealthWithOverwrite(t, "./testdata/pod-ready-container-terminated.yaml", map[string]string{ - "2024-07-18T12:03:06Z": time.Now().Add(-time.Minute * 50).UTC().Format("2006-01-02T15:04:05Z"), // container last terminated + "2024-07-18T12:03:06Z": time.Now(). + Add(-time.Minute * 50). + UTC(). + Format("2006-01-02T15:04:05Z"), + // container last terminated }, health.HealthStatusRunning, health.HealthWarning, false) // Less than 30 minutes @@ -225,17 +369,59 @@ func TestPod(t *testing.T) { assertAppHealth(t, "./testdata/pod-old-restarts.yaml", health.HealthStatusRunning, health.HealthHealthy, true) assertAppHealth(t, "./testdata/pod-pending.yaml", health.HealthStatusPending, health.HealthUnknown, false) - assertAppHealth(t, "./testdata/pod-running-not-ready.yaml", health.HealthStatusStarting, health.HealthUnknown, false) - assertAppHealth(t, "./testdata/pod-crashloop.yaml", health.HealthStatusCrashLoopBackoff, health.HealthUnhealthy, false) - assertAppHealth(t, "./testdata/pod-crashloop-pending.yaml", health.HealthStatusCrashLoopBackoff, health.HealthUnhealthy, false) + assertAppHealth( + t, + "./testdata/pod-running-not-ready.yaml", + health.HealthStatusStarting, + health.HealthUnknown, + false, + ) + assertAppHealth( + t, + "./testdata/pod-crashloop.yaml", + health.HealthStatusCrashLoopBackoff, + health.HealthUnhealthy, + false, + ) + assertAppHealth( + t, + "./testdata/pod-crashloop-pending.yaml", + health.HealthStatusCrashLoopBackoff, + health.HealthUnhealthy, + false, + ) assertAppHealth(t, "./testdata/pod-imagepullbackoff.yaml", "ImagePullBackOff", health.HealthUnhealthy, false) assertAppHealth(t, "./testdata/pod-error.yaml", health.HealthStatusError, health.HealthUnhealthy, true) - assertAppHealth(t, "./testdata/pod-running-restart-always.yaml", health.HealthStatusRunning, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/pod-running-restart-never.yaml", health.HealthStatusRunning, health.HealthHealthy, false) - assertAppHealth(t, "./testdata/pod-running-restart-onfailure.yaml", health.HealthStatusRunning, health.HealthUnhealthy, false) + assertAppHealth( + t, + "./testdata/pod-running-restart-always.yaml", + health.HealthStatusRunning, + health.HealthHealthy, + true, + ) + assertAppHealth( + t, + "./testdata/pod-running-restart-never.yaml", + health.HealthStatusRunning, + health.HealthHealthy, + false, + ) + assertAppHealth( + t, + "./testdata/pod-running-restart-onfailure.yaml", + health.HealthStatusRunning, + health.HealthUnhealthy, + false, + ) assertAppHealth(t, "./testdata/pod-failed.yaml", health.HealthStatusError, health.HealthUnhealthy, true) assertAppHealth(t, "./testdata/pod-succeeded.yaml", health.HealthStatusCompleted, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/pod-init-container-fail.yaml", health.HealthStatusCrashLoopBackoff, health.HealthUnhealthy, false) + assertAppHealth( + t, + "./testdata/pod-init-container-fail.yaml", + health.HealthStatusCrashLoopBackoff, + health.HealthUnhealthy, + false, + ) } // func TestAPIService(t *testing.T) { @@ -296,29 +482,77 @@ func TestGetArgoWorkflowHealth(t *testing.T) { } func TestArgoApplication(t *testing.T) { - assertAppHealth(t, "./testdata/argo-application-healthy.yaml", health.HealthStatusHealthy, health.HealthHealthy, true) - assertAppHealth(t, "./testdata/argo-application-missing.yaml", health.HealthStatusMissing, health.HealthUnknown, false) + assertAppHealth( + t, + "./testdata/argo-application-healthy.yaml", + health.HealthStatusHealthy, + health.HealthHealthy, + true, + ) + assertAppHealth( + t, + "./testdata/argo-application-missing.yaml", + health.HealthStatusMissing, + health.HealthUnknown, + false, + ) } func TestFluxResources(t *testing.T) { - assertAppHealthMsg(t, "./testdata/kustomization-reconciliation-failed.yaml", "ReconciliationFailed", health.HealthUnhealthy, false, "CronJob/scale-dev-up namespace not specified: the server could not find the requested resource\n") - assertAppHealthMsg(t, "./testdata/kustomization-reconciliation-failed-2.yaml", "ReconciliationFailed", health.HealthUnhealthy, false, "HelmRelease/mission-control-agent/atlas-topology dry-run failed: failed to create typed patch object (mission-control-agent/atlas-topology; helm.toolkit.fluxcd.io/v2, Kind=HelmRelease): .spec.chart.spec.targetNamespace: field not declared in schema\n") - assertAppHealth(t, "./testdata/flux-kustomization-healthy.yaml", "ReconciliationSucceeded", health.HealthHealthy, true) + assertAppHealthMsg( + t, + "./testdata/kustomization-reconciliation-failed.yaml", + "ReconciliationFailed", + health.HealthUnhealthy, + false, + "CronJob/scale-dev-up namespace not specified: the server could not find the requested resource\n", + ) + assertAppHealthMsg( + t, + "./testdata/kustomization-reconciliation-failed-2.yaml", + "ReconciliationFailed", + health.HealthUnhealthy, + false, + "HelmRelease/mission-control-agent/atlas-topology dry-run failed: failed to create typed patch object (mission-control-agent/atlas-topology; helm.toolkit.fluxcd.io/v2, Kind=HelmRelease): .spec.chart.spec.targetNamespace: field not declared in schema\n", + ) + assertAppHealth( + t, + "./testdata/flux-kustomization-healthy.yaml", + "ReconciliationSucceeded", + health.HealthHealthy, + true, + ) assertAppHealth(t, "./testdata/flux-kustomization-unhealthy.yaml", "Progressing", health.HealthUnknown, false) assertAppHealth(t, "./testdata/flux-kustomization-failed.yaml", "BuildFailed", health.HealthUnhealthy, false) status := getHealthStatus("./testdata/flux-kustomization-failed.yaml", t, nil) assert.Contains(t, status.Message, "err='accumulating resources from 'kubernetes_resource_ingress_fail.yaml'") - assertAppHealth(t, "./testdata/flux-helmrelease-healthy.yaml", "ReconciliationSucceeded", health.HealthHealthy, true) + assertAppHealth( + t, + "./testdata/flux-helmrelease-healthy.yaml", + "ReconciliationSucceeded", + health.HealthHealthy, + true, + ) assertAppHealth(t, "./testdata/flux-helmrelease-unhealthy.yaml", "UpgradeFailed", health.HealthUnhealthy, true) assertAppHealth(t, "./testdata/flux-helmrelease-upgradefailed.yaml", "UpgradeFailed", health.HealthUnhealthy, true) helmreleaseStatus := getHealthStatus("./testdata/flux-helmrelease-upgradefailed.yaml", t, nil) - assert.Contains(t, helmreleaseStatus.Message, "Helm upgrade failed for release mission-control-agent/prod-kubernetes-bundle with chart mission-control-kubernetes@0.1.29: YAML parse error on mission-control-kubernetes/templates/topology.yaml: error converting YAML to JSON: yaml: line 171: did not find expected '-' indicator") + assert.Contains( + t, + helmreleaseStatus.Message, + "Helm upgrade failed for release mission-control-agent/prod-kubernetes-bundle with chart mission-control-kubernetes@0.1.29: YAML parse error on mission-control-kubernetes/templates/topology.yaml: error converting YAML to JSON: yaml: line 171: did not find expected '-' indicator", + ) assert.Equal(t, helmreleaseStatus.Status, health.HealthStatusUpgradeFailed) assertAppHealth(t, "./testdata/flux-helmrepository-healthy.yaml", "Succeeded", health.HealthHealthy, true) assertAppHealth(t, "./testdata/flux-helmrepository-unhealthy.yaml", "Failed", health.HealthUnhealthy, false) assertAppHealth(t, "./testdata/flux-gitrepository-healthy.yaml", "Succeeded", health.HealthHealthy, true) - assertAppHealth(t, "./testdata/flux-gitrepository-unhealthy.yaml", "GitOperationFailed", health.HealthUnhealthy, false) + assertAppHealth( + t, + "./testdata/flux-gitrepository-unhealthy.yaml", + "GitOperationFailed", + health.HealthUnhealthy, + false, + ) } diff --git a/pkg/health/status.go b/pkg/health/status.go index 7d4c028..d853a56 100644 --- a/pkg/health/status.go +++ b/pkg/health/status.go @@ -86,13 +86,13 @@ type OnCondition struct { // When 2 conditions are true, which one takes precedence from a status/message perspective Order int `yaml:"order,omitempty" json:"order,omitempty"` // If the condition matches, mark ready - Ready bool `json:"ready" yaml:"ready"` + Ready bool `yaml:"ready" json:"ready"` // If the condition matches, mark not ready NotReady bool `json:"notReady" yaml:"notReady,omitempty"` // If the condition is true, use the conditions message - Message bool `json:"message" yaml:"message"` + Message bool `json:"message" yaml:"message"` Health Health `json:"health,omitempty" yaml:"health,omitempty"` // Health to set if the condition is false @@ -137,7 +137,7 @@ func (mapped *OnCondition) Apply(health *HealthStatus, c *metav1.Condition) { type Condition struct { OnCondition `yaml:",inline" json:",inline"` - OnFalse *OnCondition `yaml:"onFalse,omitempty" json:"onFalse,omitempty"` + OnFalse *OnCondition `yaml:"onFalse,omitempty" json:"onFalse,omitempty"` OnUnknown *OnCondition `yaml:"onUnknown,omitempty" json:"onUnknown,omitempty"` // Custom settings per reason @@ -180,7 +180,7 @@ func (mapped *Condition) Apply(health *HealthStatus, c *metav1.Condition) { } type StatusMap struct { - Conditions map[string]Condition `yaml:"conditions" json:"conditions"` + Conditions map[string]Condition `yaml:"conditions" json:"conditions"` UnhealthyIsNotReady bool `yaml:"unhealthyIsNotReady" json:"unhealthyIsNotReady"` } diff --git a/pkg/health/utils.go b/pkg/health/utils.go index 8920cb2..d4f3edd 100644 --- a/pkg/health/utils.go +++ b/pkg/health/utils.go @@ -32,7 +32,7 @@ type HealthStatus struct { Ready bool `json:"ready"` Health Health `json:"health"` // Status holds the status code of the application or resource - Status HealthStatusCode `json:"status,omitempty" protobuf:"bytes,1,opt,name=status"` + Status HealthStatusCode `json:"status,omitempty" protobuf:"bytes,1,opt,name=status"` // Message is a human-readable informational message describing the health status Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"` @@ -72,7 +72,8 @@ func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) boo c := getPodReadyCondition(pod.Status) minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second - if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { + if minReadySeconds == 0 || + !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { return true } return false @@ -107,7 +108,10 @@ func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodCondition // GetPodConditionFromList extracts the provided condition from the given list of condition and // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present. -func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) { +func getPodConditionFromList( + conditions []corev1.PodCondition, + conditionType corev1.PodConditionType, +) (int, *corev1.PodCondition) { if conditions == nil { return -1, nil } diff --git a/pkg/lua/custom_actions_test.go b/pkg/lua/custom_actions_test.go index a1d29a1..afcec6a 100644 --- a/pkg/lua/custom_actions_test.go +++ b/pkg/lua/custom_actions_test.go @@ -92,8 +92,11 @@ func TestLuaResourceActionsScript(t *testing.T) { // Some resources' name is derived from the source object name, so the returned name is not actually equal to the testdata output name // Considering the resource found in the testdata output if its name starts with source object name // TODO: maybe this should use a normalizer function instead of hard-coding the resource specifics here - if (result.GetKind() == "Job" && sourceObj.GetKind() == "CronJob") || (result.GetKind() == "Workflow" && (sourceObj.GetKind() == "CronWorkflow" || sourceObj.GetKind() == "WorkflowTemplate")) { - return u.GroupVersionKind() == result.GroupVersionKind() && strings.HasPrefix(u.GetName(), sourceObj.GetName()) && u.GetNamespace() == result.GetNamespace() + if (result.GetKind() == "Job" && sourceObj.GetKind() == "CronJob") || + (result.GetKind() == "Workflow" && (sourceObj.GetKind() == "CronWorkflow" || sourceObj.GetKind() == "WorkflowTemplate")) { + return u.GroupVersionKind() == result.GroupVersionKind() && + strings.HasPrefix(u.GetName(), sourceObj.GetName()) && + u.GetNamespace() == result.GetNamespace() } else { return u.GroupVersionKind() == result.GroupVersionKind() && u.GetName() == result.GetName() && u.GetNamespace() == result.GetNamespace() } @@ -175,7 +178,10 @@ func getExpectedObjectList(t *testing.T, path string) *unstructured.Unstructured return unstructuredList } -func findFirstMatchingItem(items []unstructured.Unstructured, f func(unstructured.Unstructured) bool) *unstructured.Unstructured { +func findFirstMatchingItem( + items []unstructured.Unstructured, + f func(unstructured.Unstructured) bool, +) *unstructured.Unstructured { var matching *unstructured.Unstructured = nil for _, item := range items { if f(item) { diff --git a/pkg/lua/lua.go b/pkg/lua/lua.go index 45f2141..17c181c 100644 --- a/pkg/lua/lua.go +++ b/pkg/lua/lua.go @@ -30,7 +30,9 @@ const ( type ResourceHealthOverrides map[string]ResourceOverride -func (overrides ResourceHealthOverrides) GetResourceHealth(obj *unstructured.Unstructured) (*health.HealthStatus, error) { +func (overrides ResourceHealthOverrides) GetResourceHealth( + obj *unstructured.Unstructured, +) (*health.HealthStatus, error) { luaVM := VM{ ResourceOverrides: overrides, } @@ -207,7 +209,10 @@ func (vm VM) ExecuteResourceAction(obj *unstructured.Unstructured, script string for _, impactedResource := range impactedResources { // Cleaning the resource is only relevant to "patch" if impactedResource.K8SOperation == PatchOperation { - impactedResource.UnstructuredObj.Object = cleanReturnedObj(impactedResource.UnstructuredObj.Object, obj.Object) + impactedResource.UnstructuredObj.Object = cleanReturnedObj( + impactedResource.UnstructuredObj.Object, + obj.Object, + ) } } diff --git a/pkg/lua/resources.go b/pkg/lua/resources.go index 4fed7fd..6740979 100644 --- a/pkg/lua/resources.go +++ b/pkg/lua/resources.go @@ -9,16 +9,16 @@ import ( type KnownTypeField struct { Field string `json:"field,omitempty" protobuf:"bytes,1,opt,name=field"` - Type string `json:"type,omitempty" protobuf:"bytes,2,opt,name=type"` + Type string `json:"type,omitempty" protobuf:"bytes,2,opt,name=type"` } // OverrideIgnoreDiff contains configurations about how fields should be ignored during diffs between // the desired state and live state type OverrideIgnoreDiff struct { // JSONPointers is a JSON path list following the format defined in RFC4627 (https://datatracker.ietf.org/doc/html/rfc6902#section-3) - JSONPointers []string `json:"jsonPointers" protobuf:"bytes,1,rep,name=jSONPointers"` + JSONPointers []string `json:"jsonPointers" protobuf:"bytes,1,rep,name=jSONPointers"` // JQPathExpressions is a JQ path list that will be evaludated during the diff process - JQPathExpressions []string `json:"jqPathExpressions" protobuf:"bytes,2,opt,name=jqPathExpressions"` + JQPathExpressions []string `json:"jqPathExpressions" protobuf:"bytes,2,opt,name=jqPathExpressions"` // ManagedFieldsManagers is a list of trusted managers. Fields mutated by those managers will take precedence over the // desired state defined in the SCM and won't be displayed in diffs ManagedFieldsManagers []string `json:"managedFieldsManagers" protobuf:"bytes,3,opt,name=managedFieldsManagers"` @@ -75,7 +75,14 @@ func (s ResourceOverride) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - raw := &rawResourceOverride{s.HealthLua, s.UseOpenLibs, s.Actions, string(ignoreDifferencesData), string(ignoreResourceUpdatesData), s.KnownTypeFields} + raw := &rawResourceOverride{ + s.HealthLua, + s.UseOpenLibs, + s.Actions, + string(ignoreDifferencesData), + string(ignoreResourceUpdatesData), + s.KnownTypeFields, + } return json.Marshal(raw) } @@ -93,30 +100,30 @@ func (o *ResourceOverride) GetActions() (ResourceActions, error) { // TODO: describe members of this type type ResourceActions struct { ActionDiscoveryLua string `json:"discovery.lua,omitempty" yaml:"discovery.lua,omitempty" protobuf:"bytes,1,opt,name=actionDiscoveryLua"` - Definitions []ResourceActionDefinition `json:"definitions,omitempty" protobuf:"bytes,2,rep,name=definitions"` + Definitions []ResourceActionDefinition `json:"definitions,omitempty" protobuf:"bytes,2,rep,name=definitions"` } // TODO: describe this type // TODO: describe members of this type type ResourceActionDefinition struct { - Name string `json:"name" protobuf:"bytes,1,opt,name=name"` - ActionLua string `json:"action.lua" yaml:"action.lua" protobuf:"bytes,2,opt,name=actionLua"` + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + ActionLua string `json:"action.lua" protobuf:"bytes,2,opt,name=actionLua" yaml:"action.lua"` } // TODO: describe this type // TODO: describe members of this type type ResourceAction struct { - Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` - Params []ResourceActionParam `json:"params,omitempty" protobuf:"bytes,2,rep,name=params"` + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Params []ResourceActionParam `json:"params,omitempty" protobuf:"bytes,2,rep,name=params"` Disabled bool `json:"disabled,omitempty" protobuf:"varint,3,opt,name=disabled"` } // TODO: describe this type // TODO: describe members of this type type ResourceActionParam struct { - Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` - Value string `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"` - Type string `json:"type,omitempty" protobuf:"bytes,3,opt,name=type"` + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Value string `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"` + Type string `json:"type,omitempty" protobuf:"bytes,3,opt,name=type"` Default string `json:"default,omitempty" protobuf:"bytes,4,opt,name=default"` }