Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: application set handler #241

Merged
merged 16 commits into from
Jul 31, 2024
6 changes: 6 additions & 0 deletions charts/kubechecks-rbac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# kubechecks-rbac

This chart deploys the Cluster Role and Cluster Role binding for the kubechecks running outside of existing cluster.

It is not required if you're operating all within the same cluster.

4 changes: 2 additions & 2 deletions charts/kubechecks-rbac/templates/role.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
kind: ClusterRole
metadata:
name: {{ .Values.roleName | default "kubechecks-remote-role" }}
name: {{ .Values.ClusterRoleName | default "kubechecks-remote-clusterrole" }}
rules:
- apiGroups: ['argoproj.io']
resources: ['applications', 'appprojects', 'applicationsets', 'services']
Expand Down
20 changes: 6 additions & 14 deletions charts/kubechecks-rbac/templates/rolebinding.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
kind: ClusterRoleBinding
metadata:
name: {{ .Values.roleBindingName | default "kubechecks-remote-role-binding" }}
name: {{ .Values.ClusterRoleBindingName | default "kubechecks-remote-role-binding" }}
namespace: {{ .Values.namespace | default "argocd" }}
subjects:
{{- if .Values.eks.iamRoleArn }}
- kind: User
name: {{ .Values.eks.iamRoleArn }}
- kind: Group
apiGroup: rbac.authorization.k8s.io
{{- else if .Values.selfManaged.serviceAccountName }}
- kind: ServiceAccount
name: {{ .Values.selfManaged.serviceAccountName }}
namespace: {{ .Values.namespace | default "argocd" }}
{{- else }}
{{ fail "Either eks.iamRoleArn, or selfManaged.serviceAccountName must be provided" }}
{{- end }}
name: {{ .Values.ClusterRoleBindingGroup | default "kubechecks-remote-group" }}
roleRef:
kind: Role
name: {{ .Values.roleName | default "kubechecks-remote-role" }}
kind: ClusterRole
name: {{ .Values.ClusterRoleName | default "kubechecks-remote-role" }}
apiGroup: rbac.authorization.k8s.io
39 changes: 10 additions & 29 deletions charts/kubechecks-rbac/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,27 @@
"title": "Kubechecks Values Schema",
"type": "object",
"properties": {
"roleName": {
"ClusterRoleName": {
"type": "string",
"description": "The name of the Role to be created.",
"description": "The name of the Cluster Role to be created.",
"default": "kubechecks-remote-role"
},
"roleBindingName": {
"ClusterRoleBindingName": {
"type": "string",
"description": "The name of the RoleBinding to be created.",
"description": "The name of the ClusterRoleBinding to be created.",
"default": "kubechecks-remote-role-binding"
},
"ClusterRoleBindingGroup": {
"type": "string",
"description": "The name of the Group to be created.",
"default": "kubechecks-remote-group"
},
"namespace": {
"type": "string",
"description": "The namespace where the Role and RoleBinding will be created.",
"default": "argocd"
},
"eks": {
"type": "object",
"description": "Configuration for AWS EKS clusters.",
"properties": {
"iamRoleArn": {
"type": "string",
"description": "The ARN of the IAM role to bind to the Role."
}
},
"required": ["iamRoleArn"],
"additionalProperties": false
},
"selfManaged": {
"type": "object",
"description": "Configuration for self-managed clusters.",
"properties": {
"serviceAccountName": {
"type": "string",
"description": "The name of the ServiceAccount to bind to the Role."
}
},
"required": ["serviceAccountName"],
"additionalProperties": false
}
},
"required": ["roleName", "roleBindingName", "namespace"],
"required": ["ClusterRoleName", "ClusterRoleBindingName", "ClusterRoleBindingGroup", "namespace"],
"additionalProperties": false
}
17 changes: 5 additions & 12 deletions charts/kubechecks-rbac/values.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
roleName: "kubechecks-remote-role"
roleBindingName: "kubechecks-remote-role-binding"
namespace: "argocd"

# for AWS EKS clusters
eks:
# The ARN of the IAM role to bind to the Role
iamRoleArn: ""
ClusterRoleName: "kubechecks-remote-role"
ClusterRoleBindingName: "kubechecks-remote-role-binding"
ClusterRoleBindingGroup: "kubechecks-remote-group"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should start with a lowercase letter, for consistency

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, changed to lowercase


# for self-managed clusters
selfManaged:
# The name of the ServiceAccount to bind to the Role
serviceAccountName: ""
# namespace to create the ClusterRole and RoleBinding, this has to match the argocd is operating.
namespace: "argocd"
2 changes: 1 addition & 1 deletion cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func newContainer(ctx context.Context, cfg config.ServerConfig, watchApps bool)
KubernetesConfigPath: cfg.KubernetesConfig,
ClusterType: cfg.KubernetesType,
},
client.EKSClientOption(ctx, cfg.KubernetesClusterID, cfg.KubernetesClusterRegion),
client.EKSClientOption(ctx, cfg.KubernetesClusterID),
)
if err != nil {
return ctr, errors.Wrap(err, "failed to create kube client")
Expand Down
1 change: 0 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ func init() {
withChoices("eks", "local").
withDefault("local"))
stringFlag(flags, "kubernetes-clusterid", "Kubernetes Cluster ID, must be specified if kubernetes-type is eks.")
stringFlag(flags, "kubernetes-cluster-region", "Kubernetes Cluster Region, if the cluster is on a cloud provider, and running on a different region, this must be specified.")
stringFlag(flags, "kubernetes-config", "Path to your kubernetes config file, used to monitor applications.")

stringFlag(flags, "otel-collector-port", "The OpenTelemetry collector port.")
Expand Down
3 changes: 1 addition & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ By abstracting the PR/MR in this way, `kubechecks` remains VCS provider agnostic

The final piece of the puzzle is the `CheckEvent`; an internal structure that takes a `Client` and a `Repo` and begins running all configured checks. A `CheckEvent` first determines what applications within the repository have been affected by the PR/MR, and begins concurrently running the check suite against each affected application to generate a report for that app. As each application updates its report, the `CheckEvent` compiles all reports together and instructs the `Client` to update the PR/MR with a comment detailing the current progress; resulting in one comment per run of `kubechecks` with the latest information about that particular run. Whenever a new run of `kubechecks` is initiated, all previous comments are deleted to reduce clutter.


### Event Flows Diagram
### Event Flow Diagram

![Event Flow Diagram](./img/eventflowdiagram.png){: style="height:350px;display:block;margin:0 auto;"}

Expand Down
1 change: 0 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ The full list of supported environment variables is described below:
|`KUBECHECKS_ENABLE_PREUPGRADE`|Enable preupgrade checks.|`true`|
|`KUBECHECKS_ENSURE_WEBHOOKS`|Ensure that webhooks are created in repositories referenced by argo.|`false`|
|`KUBECHECKS_FALLBACK_K8S_VERSION`|Fallback target Kubernetes version for schema / upgrade checks.|`1.23.0`|
|`KUBECHECKS_KUBERNETES_CLUSTER_REGION`|Kubernetes Cluster Region, if the cluster is on a cloud provider, and running on a different region, this must be specified.||
|`KUBECHECKS_KUBERNETES_CLUSTERID`|Kubernetes Cluster ID, must be specified if kubernetes-type is eks.||
|`KUBECHECKS_KUBERNETES_CONFIG`|Path to your kubernetes config file, used to monitor applications.||
|`KUBECHECKS_KUBERNETES_TYPE`|Kubernetes Type One of eks, or local.|`local`|
Expand Down
6 changes: 5 additions & 1 deletion pkg/app_watcher/app_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ type ApplicationWatcher struct {
vcsToArgoMap appdir.VcsToArgoMap
}

// NewApplicationWatcher creates new instance of ApplicationWatcher.
// NewApplicationWatcher creates a new instance of ApplicationWatcher.
//
// - kubeCfg is the Kubernetes configuration.
// - vcsToArgoMap is the mapping between VCS and Argo applications.
// - cfg is the server configuration.
func NewApplicationWatcher(kubeCfg *rest.Config, vcsToArgoMap appdir.VcsToArgoMap, cfg config.ServerConfig) (*ApplicationWatcher, error) {
if kubeCfg == nil {
return nil, fmt.Errorf("kubeCfg cannot be nil")
Expand Down
8 changes: 5 additions & 3 deletions pkg/appdir/app_directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ func (d *AppDirectory) ProcessApp(app v1alpha1.Application) {
}
}

// FindAppsBasedOnChangeList receives the modified file path and
// FindAppsBasedOnChangeList receives a list of modified file paths and
// returns the list of applications that are affected by the changes.
//
// e.g. changeList = ["path/to/file1", "path/to/file2"]
// changeList: a slice of strings representing the paths of modified files.
// targetBranch: the branch name to compare against the target revision of the applications.
// e.g. changeList = ["path/to/file1", "path/to/file2"]
func (d *AppDirectory) FindAppsBasedOnChangeList(changeList []string, targetBranch string) []v1alpha1.Application {
log.Debug().Msgf("checking %d changes", len(changeList))

Expand Down Expand Up @@ -150,7 +152,7 @@ func (d *AppDirectory) GetApps(filter func(stub v1alpha1.Application) bool) []v1

func (d *AppDirectory) AddApp(app v1alpha1.Application) {
if _, exists := d.appsMap[app.Name]; exists {
log.Info().Msgf("app %s already exists", app.Name)
log.Debug().Msgf("app %s already exists", app.Name)
return
}
log.Debug().
Expand Down
4 changes: 2 additions & 2 deletions pkg/argo_client/applications.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ func (argo *ArgoClient) GetApplicationSets(ctx context.Context) (*v1alpha1.Appli

resp, err := appClient.List(ctx, new(applicationset.ApplicationSetListQuery))
if err != nil {
telemetry.SetError(span, err, "Argo List All Applications error")
return nil, errors.Wrap(err, "failed to applications")
telemetry.SetError(span, err, "Argo List All Application Sets error")
return nil, errors.Wrap(err, "failed to application sets")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should be "failed to list application sets"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it to failed to list application sets.

}
return resp, nil
}
18 changes: 9 additions & 9 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ import (

type ServerConfig struct {
// argocd
ArgoCDServerAddr string `mapstructure:"argocd-api-server-addr"`
ArgoCDToken string `mapstructure:"argocd-api-token"`
ArgoCDPathPrefix string `mapstructure:"argocd-api-path-prefix"`
ArgoCDInsecure bool `mapstructure:"argocd-api-insecure"`
ArgoCDNamespace string `mapstructure:"argocd-api-namespace"`
KubernetesConfig string `mapstructure:"kubernetes-config"`
KubernetesType string `mapstructure:"kubernetes-type"`
KubernetesClusterID string `mapstructure:"kubernetes-clusterid"`
KubernetesClusterRegion string `mapstructure:"kubernetes-cluster-region"`
ArgoCDServerAddr string `mapstructure:"argocd-api-server-addr"`
ArgoCDToken string `mapstructure:"argocd-api-token"`
ArgoCDPathPrefix string `mapstructure:"argocd-api-path-prefix"`
ArgoCDInsecure bool `mapstructure:"argocd-api-insecure"`
ArgoCDNamespace string `mapstructure:"argocd-api-namespace"`
KubernetesConfig string `mapstructure:"kubernetes-config"`
KubernetesType string `mapstructure:"kubernetes-type"`
KubernetesClusterID string `mapstructure:"kubernetes-clusterid"`

// otel
EnableOtel bool `mapstructure:"otel-enabled"`
OtelCollectorHost string `mapstructure:"otel-collector-host"`
Expand Down
20 changes: 5 additions & 15 deletions pkg/kubernetes/api_eks_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ func appendPresignHeaderValuesFunc(clusterID string) func(stsOptions *sts.Option
// - ctx: the context for the AWS SDK client.
//
// - clusterID: the name of the EKS cluster.
//
// - region: the AWS region where the EKS cluster is located. (optional) If not provided, the default region will be used.
func EKSClientOption(ctx context.Context, clusterID, region string) NewClientOption {
func EKSClientOption(ctx context.Context, clusterID string) NewClientOption {
return func(c *NewClientInput) {
if c.ClusterType != ClusterTypeEKS {
slog.Error("incorrect cluster type from the ClientInput, type must be ClusterTypeEKS", "ClusterType", c.ClusterType)
Expand All @@ -79,18 +77,10 @@ func EKSClientOption(ctx context.Context, clusterID, region string) NewClientOpt
err error
)

if region != "" {
cfg, err = config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil {
slog.Error("unable to load AWS SDK config", "err", err)
return
}
} else {
cfg, err = config.LoadDefaultConfig(ctx)
if err != nil {
slog.Error("unable to load AWS SDK config", "err", err)
return
}
cfg, err = config.LoadDefaultConfig(ctx)
if err != nil {
slog.Error("unable to load AWS SDK config", "err", err)
return
}
eksClient := eks.NewFromConfig(cfg)
// perform DescribeCluster to get endpoint address and CA data.
Expand Down