Skip to content

Commit

Permalink
Merge branch 'master' into fix-argoproj#15319
Browse files Browse the repository at this point in the history
  • Loading branch information
timgriffiths authored Nov 24, 2024
2 parents 8640ebc + ff08643 commit c9a2d43
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 16 deletions.
14 changes: 11 additions & 3 deletions cmd/argocd/commands/applicationset.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
errors.CheckError(err)
case "wide", "":
printAppSetSummaryTable(appSet)

if len(appSet.Status.Conditions) > 0 {
fmt.Println()
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
Expand Down Expand Up @@ -435,7 +434,6 @@ func getServerForAppSet(appSet *arogappsetv1.ApplicationSet) string {
}

func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
source := appSet.Spec.Template.Spec.GetSource()
fmt.Printf(printOpFmtStr, "Name:", appSet.QualifiedName())
fmt.Printf(printOpFmtStr, "Project:", appSet.Spec.Template.Spec.GetProject())
fmt.Printf(printOpFmtStr, "Server:", getServerForAppSet(appSet))
Expand All @@ -445,7 +443,17 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
} else {
fmt.Println("Sources:")
}
printAppSourceDetails(&source)

// if no source has been defined, print the default value for a source
if len(appSet.Spec.Template.Spec.GetSources()) == 0 {
src := appSet.Spec.Template.Spec.GetSource()
printAppSourceDetails(&src)
} else {
// otherwise range over the sources and print each source details
for _, source := range appSet.Spec.Template.Spec.GetSources() {
printAppSourceDetails(&source)
}
}

var (
syncPolicyStr string
Expand Down
51 changes: 51 additions & 0 deletions cmd/argocd/commands/applicationset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,26 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
},
},
}
appsetSpecSource := baseAppSet.DeepCopy()
appsetSpecSource.Spec.Template.Spec.Source = &v1alpha1.ApplicationSource{
RepoURL: "test1",
TargetRevision: "master1",
Path: "/test1",
}

appsetSpecSources := baseAppSet.DeepCopy()
appsetSpecSources.Spec.Template.Spec.Sources = v1alpha1.ApplicationSources{
{
RepoURL: "test1",
TargetRevision: "master1",
Path: "/test1",
},
{
RepoURL: "test2",
TargetRevision: "master2",
Path: "/test2",
},
}

appsetSpecSyncPolicy := baseAppSet.DeepCopy()
appsetSpecSyncPolicy.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{
Expand Down Expand Up @@ -210,6 +230,37 @@ Source:
- Repo:
Target:
SyncPolicy: Automated
`,
},
{
name: "appset with a single source",
appSet: appsetSpecSource,
expectedOutput: `Name: app-name
Project: default
Server:
Namespace:
Source:
- Repo: test1
Target: master1
Path: /test1
SyncPolicy: <none>
`,
},
{
name: "appset with a multiple sources",
appSet: appsetSpecSources,
expectedOutput: `Name: app-name
Project: default
Server:
Namespace:
Sources:
- Repo: test1
Target: master1
Path: /test1
- Repo: test2
Target: master2
Path: /test2
SyncPolicy: <none>
`,
},
} {
Expand Down
4 changes: 4 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ const (
// AnnotationCompareOptions is a comma-separated list of options for comparison
AnnotationCompareOptions = "argocd.argoproj.io/compare-options"

// AnnotationIgnoreHealthCheck when set on an Application's immediate child indicates that its health check
// can be disregarded.
AnnotationIgnoreHealthCheck = "argocd.argoproj.io/ignore-healthcheck"

// AnnotationKeyManagedBy is annotation name which indicates that k8s resource is managed by an application.
AnnotationKeyManagedBy = "managed-by"
// AnnotationValueManagedByArgoCD is a 'managed-by' annotation value for resources managed by Argo CD
Expand Down
4 changes: 4 additions & 0 deletions controller/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/lua"
Expand All @@ -24,6 +25,9 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource
if res.Target != nil && hookutil.Skip(res.Target) {
continue
}
if res.Target != nil && res.Target.GetAnnotations() != nil && res.Target.GetAnnotations()[common.AnnotationIgnoreHealthCheck] == "true" {
continue
}

if res.Live != nil && (hookutil.IsHook(res.Live) || ignore.Ignore(res.Live)) {
continue
Expand Down
9 changes: 9 additions & 0 deletions controller/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ func TestSetApplicationHealth(t *testing.T) {
healthStatus, err = setApplicationHealth(resources, resourceStatuses, nil, app, true)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)

// now we set the `argocd.argoproj.io/ignore-healthcheck: "true"` annotation on the job's target.
// The app is considered healthy
failedJob.SetAnnotations(nil)
failedJobIgnoreHealthcheck := resourceFromFile("./testdata/job-failed-ignore-healthcheck.yaml")
resources[1].Target = &failedJobIgnoreHealthcheck
healthStatus, err = setApplicationHealth(resources, resourceStatuses, nil, app, true)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
}

func TestSetApplicationHealth_ResourceHealthNotPersisted(t *testing.T) {
Expand Down
36 changes: 36 additions & 0 deletions controller/testdata/job-failed-ignore-healthcheck.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: batch/v1
kind: Job
metadata:
annotations:
argocd.argoproj.io/ignore-healthcheck: "true"
labels:
job-name: fail
name: fail
namespace: argoci-workflows
selfLink: /apis/batch/v1/namespaces/argoci-workflows/jobs/fail
spec:
backoffLimit: 0
completions: 1
parallelism: 1
template:
metadata:
creationTimestamp: null
labels:
job-name: fail
spec:
containers:
- command:
- sh
- -c
- exit 1
image: alpine:latest
imagePullPolicy: Always
name: fail
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
13 changes: 13 additions & 0 deletions docs/operator-manual/health.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,16 @@ App (healthy)
└── CustomResource (healthy) <- This resource's health check needs to be fixed to mark the App as unhealthy
└── CustomChildResource (unhealthy)
```
## Ignoring Child Resource Health Check in Applications
To ignore the health check of an immediate child resource within an Application, set the annotation `argocd.argoproj.io/ignore-healthcheck` to `true`. For example:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
argocd.argoproj.io/ignore-healthcheck: "true"
```

By doing this, the health status of the Deployment will not affect the health of its parent Application.
1 change: 1 addition & 0 deletions docs/operator-manual/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Metrics about applications. Scraped at the `argocd-metrics:8082/metrics` endpoin
| `argocd_app_condition` | gauge | Report Applications conditions. It contains the conditions currently present in the application status. |
| `argocd_app_k8s_request_total` | counter | Number of Kubernetes requests executed during application reconciliation |
| `argocd_app_labels` | gauge | Argo Application labels converted to Prometheus labels. Disabled by default. See section below about how to enable it. |
| `argocd_app_orphaned_resources_count` | gauge | Number of orphaned resources per application. |
| `argocd_app_reconcile` | histogram | Application reconciliation performance in seconds. |
| `argocd_app_sync_total` | counter | Counter for application sync history |
| `argocd_cluster_api_resource_objects` | gauge | Number of k8s resource objects in the cache. |
Expand Down
4 changes: 2 additions & 2 deletions docs/operator-manual/resource_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ The `discovery.lua` script must return a table where the key name represents the

Each action name must be represented in the list of `definitions` with an accompanying `action.lua` script to control the resource modifications. The `obj` is a global variable which contains the resource. Each action script returns an optionally modified version of the resource. In this example, we are simply setting `.spec.suspend` to either `true` or `false`.

By default, defining a resource action customization will override any built-in action for this resource kind. If you want to retain the built-in actions, you can set the `mergeBuiltinActions` key to `true`. Your custom actions will have precedence over the built-in actions.
By default, defining a resource action customization will override any built-in action for this resource kind. As of Argo CD version 2.13.0, if you want to retain the built-in actions, you can set the `mergeBuiltinActions` key to `true`. Your custom actions will have precedence over the built-in actions.
```yaml
resource.customizations.actions.argoproj.io_Rollout: |
mergeBuiltinActions: true
Expand Down Expand Up @@ -202,4 +202,4 @@ resource.customizations.actions.ConfigMap: |
result[1] = impactedResource1
result[2] = impactedResource2
return result
```
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ require (
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427
oras.land/oras-go/v2 v2.5.0
sigs.k8s.io/controller-runtime v0.19.1
sigs.k8s.io/controller-runtime v0.19.2
sigs.k8s.io/structured-merge-diff/v4 v4.4.1
sigs.k8s.io/yaml v1.4.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1454,8 +1454,8 @@ nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk=
sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/controller-runtime v0.19.2 h1:3sPrF58XQEPzbE8T81TN6selQIMGbtYwuaJ6eDssDF8=
sigs.k8s.io/controller-runtime v0.19.2/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
Expand Down
56 changes: 48 additions & 8 deletions pkg/apis/application/v1alpha1/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,100 +107,140 @@ func TestAppProject_IsNegatedSourcePermitted(t *testing.T) {
}

func TestAppProject_IsDestinationPermitted(t *testing.T) {
t.Parallel()

testData := []struct {
name string
projDest []ApplicationDestination
appDest ApplicationDestination
isPermitted bool
}{
{
name: "server an namespace match",
projDest: []ApplicationDestination{{
Server: "https://kubernetes.default.svc", Namespace: "default",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"},
isPermitted: true,
},
{
name: "namespace does not match",
projDest: []ApplicationDestination{{
Server: "https://kubernetes.default.svc", Namespace: "default",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"},
isPermitted: false,
},
{
name: "server does not match",
projDest: []ApplicationDestination{{
Server: "https://my-cluster", Namespace: "default",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"},
isPermitted: false,
},
{
name: "wildcard namespace",
projDest: []ApplicationDestination{{
Server: "https://kubernetes.default.svc", Namespace: "*",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"},
isPermitted: true,
},
{
name: "wildcard server",
projDest: []ApplicationDestination{{
Server: "https://*.default.svc", Namespace: "default",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"},
isPermitted: true,
},
{
name: "wildcard server and namespace",
projDest: []ApplicationDestination{{
Server: "https://team1-*", Namespace: "default",
}},
appDest: ApplicationDestination{Server: "https://test2-dev-cluster", Namespace: "default"},
isPermitted: false,
},
{
name: "wildcard namespace with prefix",
projDest: []ApplicationDestination{{
Server: "https://kubernetes.default.svc", Namespace: "test-*",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test-foo"},
isPermitted: true,
},
{
name: "wildcard namespace without prefix",
projDest: []ApplicationDestination{{
Server: "https://kubernetes.default.svc", Namespace: "test-*",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"},
isPermitted: false,
},
{
name: "wildcard server and namespace",
projDest: []ApplicationDestination{{
Server: "*", Namespace: "*",
}},
appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"},
isPermitted: true,
},
{
name: "wildcard server and namespace with name",
projDest: []ApplicationDestination{{
Server: "", Namespace: "*", Name: "test",
}},
appDest: ApplicationDestination{Name: "test", Namespace: "test"},
isPermitted: true,
},
{
name: "wildcard server and namespace with different name",
projDest: []ApplicationDestination{{
Server: "", Namespace: "*", Name: "test2",
}},
appDest: ApplicationDestination{Name: "test", Namespace: "test"},
isPermitted: false,
},
/**
- name: host-cluster
namespace: '!{kube-system,argocd}'
server: 'https://kubernetes.default.svc'
- name: destination-cluster-01
namespace: '*'
server: 'https://eks-cluster-endpoint.ap-southeast-1.eks.amazonaws.com'
destination:
server: https://eks-cluster-endpoint.ap-southeast-1.eks.amazonaws.com
namespace: karpenter
*/
{
name: "negated namespace with multiple values",
projDest: []ApplicationDestination{
{Name: "host-cluster", Server: "https://kubernetes.default.svc", Namespace: "!{kube-system,argocd}"},
{Name: "destination-cluster-01", Server: "https://eks-cluster-endpoint.ap-southeast-1.eks.amazonaws.com", Namespace: "*"},
},
appDest: ApplicationDestination{Server: "https://eks-cluster-endpoint.ap-southeast-1.eks.amazonaws.com", Namespace: "kube-system"},
isPermitted: true,
},
}

for _, data := range testData {
proj := AppProject{
Spec: AppProjectSpec{
Destinations: data.projDest,
},
}
permitted, _ := proj.IsDestinationPermitted(data.appDest, func(project string) ([]*Cluster, error) {
return []*Cluster{}, nil
data := data
t.Run(data.name, func(t *testing.T) {
t.Parallel()

proj := AppProject{
Spec: AppProjectSpec{
Destinations: data.projDest,
},
}
permitted, _ := proj.IsDestinationPermitted(data.appDest, func(project string) ([]*Cluster, error) {
return []*Cluster{}, nil
})
assert.Equal(t, data.isPermitted, permitted)
})
assert.Equal(t, data.isPermitted, permitted)
}
}

Expand Down
Loading

0 comments on commit c9a2d43

Please sign in to comment.