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

handle multi source applications #298

Merged
merged 27 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eb33b5b
base refactoring
djeebus Nov 7, 2024
5e8ca6b
I think it ... works?
djeebus Nov 8, 2024
3711583
update markdown docs
djeebus Nov 8, 2024
4bbab33
linting, configuration
djeebus Nov 8, 2024
9e7183d
this seems to be required in a default installation
djeebus Nov 8, 2024
e72f937
docs
djeebus Nov 8, 2024
dbd9d1f
revert default log level change
djeebus Nov 8, 2024
0598e32
handle source.spec.helm.ignoreMissingValuesFiles = true (#303)
sl1pm4t Nov 8, 2024
2b224e0
allow kubechecks to access argocd settings
djeebus Nov 9, 2024
9116019
update some documentation
djeebus Nov 15, 2024
53b543c
Merge remote-tracking branch 'origin/main' into multi-source-apps
djeebus Nov 15, 2024
3c5fdeb
regenerate mocks
djeebus Nov 15, 2024
e362212
Merge main into branch
djeebus Nov 20, 2024
9395543
Merge branch 'main' into multi-source-apps
djeebus Dec 4, 2024
220c5cb
fix issues with new rollback parameters
djeebus Dec 4, 2024
f413659
unit tests and a bug fix
djeebus Dec 4, 2024
07eff3e
add support for packaging up an entire repo
djeebus Dec 6, 2024
2cd3ca0
add docs
djeebus Dec 6, 2024
33a7721
the useful parts of this function are already tested in pkg/repo_test.go
djeebus Dec 9, 2024
bf6fdb7
add type safety in lieu of tests
djeebus Dec 9, 2024
2ee19ed
add some debug info
djeebus Dec 10, 2024
9be07ae
well this was fun!
djeebus Dec 12, 2024
31a7459
clean up the test
djeebus Dec 12, 2024
5aa776e
Merge branch 'main' into multi-source-apps
djeebus Dec 12, 2024
1fd4e89
clean up test code, add some comments
djeebus Dec 12, 2024
47f7131
found another place we should ignore missing values, if necessary
djeebus Dec 12, 2024
34f6fbe
fix a bug, clean up tests
djeebus Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
with-expecter: true
dir: "mocks/{{.PackageName}}/mocks"
packages:
github.com/zapier/kubechecks/pkg/vcs:
config:
all: true
github.com/zapier/kubechecks/pkg/vcs/github_client:
# place your package-specific config here
config:
Expand Down
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ helm-cr 1.6.1
helm-ct 3.11.0
kubeconform 0.6.7
kustomize 5.5.0
mockery 2.46.3
staticcheck 2024.1.1
tilt 0.33.2
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

## Pull/Merge Request driven checks

When using ArgoCD, it can be difficult to tell just how your Pull/Merge Request (PR/MR) will impact your live deployment. `kubechecks` was designed to address this problem; every time a new PR/MR is created, `kubechecks` will automatically determine what's changed and how it will impact your `main`/default branchs state, informing you of the details directly on the PR/MR. As a bonus, it also lints and checks your Kubernetes manifests to let you know ahead of time if something is outdated, invalid, or otherwise not good practice.
When using ArgoCD, it can be difficult to tell just how your Pull/Merge Request (PR/MR) will impact your live deployment. `kubechecks` was designed to address this problem; every time a new PR/MR is created, `kubechecks` will automatically determine what's changed and how it will impact your `main`/default branch's state, informing you of the details directly on the PR/MR. As a bonus, it also lints and checks your Kubernetes manifests to let you know ahead of time if something is outdated, invalid, or otherwise not good practice.

![Demo](./docs/gif/kubechecks.gif)

### How it works

This tool provides a server function that processes webhooks from Gitlab/Github, clones the repository at the `HEAD` SHA of the PR/MR, and runs various check suites, commenting the output of each check in a single comment on your PR/MR. `kubechecks` talks directly to ArgoCD to get the live state of your deployments to ensure that you have the most accurate information about how your changes will affect your production code.
This tool provides a server function that processes webhooks from Gitlab/Github, clones the repository at the `HEAD` SHA of the PR/MR, and runs various check suites, commenting the output of each check in a single comment on your PR/MR. `kubechecks` talks directly to ArgoCD to get the live state of your deployments and talks directly to ArgoCD's repo server to generate the new resources to ensure that you have the most accurate information about how your changes will affect your production code.

### Architecture

Expand Down
2 changes: 1 addition & 1 deletion charts/kubechecks/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: kubechecks
description: A Helm chart for kubechecks
version: 0.4.6
version: 0.5.0
type: application
maintainers:
- name: zapier
15 changes: 15 additions & 0 deletions charts/kubechecks/templates/role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: kubechecks
namespace: {{ .Values.argocd.namespace }}
rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- get
- list
- watch
13 changes: 13 additions & 0 deletions charts/kubechecks/templates/rolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kubechecks
namespace: {{ .Values.argocd.namespace }}
roleRef:
kind: Role
name: kubechecks
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: {{ include "kubechecks.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
3 changes: 3 additions & 0 deletions charts/kubechecks/values.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Labels to apply to all resources created by this Helm chart
argocd:
namespace: argocd

commonLabels: {}

configMap:
Expand Down
123 changes: 0 additions & 123 deletions cmd/container.go

This file was deleted.

28 changes: 27 additions & 1 deletion cmd/controller_cmd.go → cmd/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zapier/kubechecks/pkg/app_watcher"

"github.com/zapier/kubechecks/pkg"
"github.com/zapier/kubechecks/pkg/checks"
Expand Down Expand Up @@ -41,19 +42,39 @@ var ControllerCmd = &cobra.Command{
log.Fatal().Err(err).Msg("failed to parse configuration")
}

ctr, err := newContainer(ctx, cfg, true)
ctr, err := container.New(ctx, cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to create container")
}

// watch app modifications, if necessary
if cfg.MonitorAllApplications {
appWatcher, err := app_watcher.NewApplicationWatcher(ctr)
if err != nil {
log.Fatal().Err(err).Msg("failed to create watch applications")
}
go appWatcher.Run(ctx, 1)

appSetWatcher, err := app_watcher.NewApplicationSetWatcher(ctr)
if err != nil {
log.Fatal().Err(err).Msg("failed to create watch application sets")
}
go appSetWatcher.Run(ctx)
} else {
log.Info().Msgf("not monitoring applications, MonitorAllApplications: %+v", cfg.MonitorAllApplications)
}

log.Info().Msg("initializing git settings")
if err = initializeGit(ctr); err != nil {
log.Fatal().Err(err).Msg("failed to initialize git settings")
}

log.Info().Strs("locations", cfg.PoliciesLocation).Msg("processing policies locations")
if err = processLocations(ctx, ctr, cfg.PoliciesLocation); err != nil {
log.Fatal().Err(err).Msg("failed to process policy locations")
}

log.Info().Strs("locations", cfg.SchemasLocations).Msg("processing schemas locations")
if err = processLocations(ctx, ctr, cfg.SchemasLocations); err != nil {
log.Fatal().Err(err).Msg("failed to process schema locations")
}
Expand Down Expand Up @@ -137,6 +158,11 @@ func init() {
newStringOpts().withDefault("1.23.0"))
boolFlag(flags, "show-debug-info", "Set to true to print debug info to the footer of MR comments (KUBECHECKS_SHOW_DEBUG_INFO).")

stringFlag(flags, "argocd-repository-endpoint", `Location of the argocd repository service endpoint.`,
newStringOpts().withDefault("argocd-repo-server.argocd:8081"))
boolFlag(flags, "argocd-repository-insecure", `True if you need to skip validating the grpc tls certificate.`,
newBoolOpts().withDefault(true))
boolFlag(flags, "argocd-send-full-repository", `Set to true if you want to try to send the full repository to ArgoCD when generating manifests.`)
stringFlag(flags, "label-filter", `(Optional) If set, The label that must be set on an MR (as "kubechecks:<value>") for kubechecks to process the merge request webhook (KUBECHECKS_LABEL_FILTER).`)
stringFlag(flags, "openai-api-token", "OpenAI API Token.")
stringFlag(flags, "webhook-url-base", "The endpoint to listen on for incoming PR/MR event webhooks. For example, 'https://checker.mycompany.com'.")
Expand Down
2 changes: 2 additions & 0 deletions cmd/locations.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func processLocations(ctx context.Context, ctr container.Container, locations []
}
}

log.Debug().Strs("locations", locations).Msg("locations after processing")

return nil
}

Expand Down
37 changes: 35 additions & 2 deletions cmd/process.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package cmd

import (
"os"
"path/filepath"

"github.com/argoproj/argo-cd/v2/common"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/zapier/kubechecks/pkg/container"

"github.com/zapier/kubechecks/pkg/config"
"github.com/zapier/kubechecks/pkg/server"
Expand All @@ -15,14 +20,42 @@ var processCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()

tempPath, err := os.MkdirTemp("", "")
if err != nil {
log.Fatal().Err(err).Msg("fail to create ssh data dir")
}
defer func() {
os.RemoveAll(tempPath)
}()

// symlink local ssh known hosts to argocd ssh known hosts
homeDir, err := os.UserHomeDir()
if err != nil {
log.Fatal().Err(err).Msg("failed to get user home dir")
}
source := filepath.Join(homeDir, ".ssh", "known_hosts")
target := filepath.Join(tempPath, common.DefaultSSHKnownHostsName)

if err := os.Symlink(source, target); err != nil {
log.Fatal().Err(err).Msg("fail to symlink ssh_known_hosts file")
}

if err := os.Setenv("ARGOCD_SSH_DATA_PATH", tempPath); err != nil {
log.Fatal().Err(err).Msg("fail to set ARGOCD_SSH_DATA_PATH")
}

cfg, err := config.New()
if err != nil {
log.Fatal().Err(err).Msg("failed to generate config")
}

ctr, err := newContainer(ctx, cfg, false)
if len(args) != 1 {
log.Fatal().Msg("usage: kubechecks process PR_REF")
}

ctr, err := container.New(ctx, cfg)
if err != nil {
log.Fatal().Err(err).Msg("failed to create container")
log.Fatal().Err(err).Msg("failed to create clients")
}

log.Info().Msg("initializing git settings")
Expand Down
9 changes: 5 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func init() {
newStringOpts().
withChoices("hide", "delete").
withDefault("hide"))
stringSliceFlag(flags, "schemas-location", "Sets schema locations to be used for every check request. Can be common paths inside the repos being checked or git urls in either git or http(s) format.")
stringSliceFlag(flags, "schemas-location", "Sets schema locations to be used for every check request. Can be a common path on the host or git urls in either git or http(s) format.")
boolFlag(flags, "enable-conftest", "Set to true to enable conftest policy checking of manifests.")
stringSliceFlag(flags, "policies-location", "Sets rego policy locations to be used for every check request. Can be common path inside the repos being checked or git urls in either git or http(s) format.",
newStringSliceOpts().
Expand Down Expand Up @@ -124,9 +124,6 @@ func init() {
}

func setupLogOutput() {
output := zerolog.ConsoleWriter{Out: os.Stdout}
log.Logger = log.Output(output)

// Default level is info, unless debug flag is present
levelFlag := viper.GetString("log-level")
level, err := zerolog.ParseLevel(levelFlag)
Expand All @@ -135,6 +132,10 @@ func setupLogOutput() {
}

zerolog.SetGlobalLevel(level)

output := zerolog.ConsoleWriter{Out: os.Stdout}
log.Logger = log.Output(output)

log.Debug().Msg("Debug level logging enabled.")
log.Trace().Msg("Trace level logging enabled.")
log.Info().Msg("Initialized logger.")
Expand Down
5 changes: 4 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ The full list of supported environment variables is described below:
|`KUBECHECKS_ARGOCD_API_PLAINTEXT`|Enable to use plaintext connections without TLS.|`false`|
|`KUBECHECKS_ARGOCD_API_SERVER_ADDR`|ArgoCD API Server Address.|`argocd-server`|
|`KUBECHECKS_ARGOCD_API_TOKEN`|ArgoCD API token.||
|`KUBECHECKS_ARGOCD_REPOSITORY_ENDPOINT`|Location of the argocd repository service endpoint.|`argocd-repo-server.argocd:8081`|
|`KUBECHECKS_ARGOCD_REPOSITORY_INSECURE`|True if you need to skip validating the grpc tls certificate.|`true`|
|`KUBECHECKS_ARGOCD_SEND_FULL_REPOSITORY`|Set to true if you want to try to send the full repository to ArgoCD when generating manifests.|`false`|
|`KUBECHECKS_ENABLE_CONFTEST`|Set to true to enable conftest policy checking of manifests.|`false`|
|`KUBECHECKS_ENABLE_HOOKS_RENDERER`|Render hooks.|`true`|
|`KUBECHECKS_ENABLE_KUBECONFORM`|Enable kubeconform checks.|`true`|
Expand All @@ -66,7 +69,7 @@ The full list of supported environment variables is described below:
|`KUBECHECKS_POLICIES_LOCATION`|Sets rego policy locations to be used for every check request. Can be common path inside the repos being checked or git urls in either git or http(s) format.|`[./policies]`|
|`KUBECHECKS_REPLAN_COMMENT_MSG`|comment message which re-triggers kubechecks on PR.|`kubechecks again`|
|`KUBECHECKS_REPO_REFRESH_INTERVAL`|Interval between static repo refreshes (for schemas and policies).|`5m`|
|`KUBECHECKS_SCHEMAS_LOCATION`|Sets schema locations to be used for every check request. Can be common paths inside the repos being checked or git urls in either git or http(s) format.|`[]`|
|`KUBECHECKS_SCHEMAS_LOCATION`|Sets schema locations to be used for every check request. Can be a common path on the host or git urls in either git or http(s) format.|`[]`|
|`KUBECHECKS_SHOW_DEBUG_INFO`|Set to true to print debug info to the footer of MR comments.|`false`|
|`KUBECHECKS_TIDY_OUTDATED_COMMENTS_MODE`|Sets the mode to use when tidying outdated comments. One of hide, delete.|`hide`|
|`KUBECHECKS_VCS_BASE_URL`|VCS base url, useful if self hosting gitlab, enterprise github, etc.||
Expand Down
6 changes: 5 additions & 1 deletion mocks/affected_apps/mocks/mock_Matcher.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading