diff --git a/.github/workflows/bootstrap.yaml b/.github/workflows/bootstrap.yaml index 23942d47..8c832a2b 100644 --- a/.github/workflows/bootstrap.yaml +++ b/.github/workflows/bootstrap.yaml @@ -34,12 +34,18 @@ jobs: go build -o /tmp/flux ./cmd/flux - name: Set outputs id: vars - run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + run: | + REPOSITORY_NAME=${{ github.event.repository.name }} + BRANCH_NAME=${GITHUB_REF##*/} + COMMIT_SHA=$(git rev-parse HEAD) + PSEUDO_RAND_SUFFIX=$(echo "${BRANCH_NAME}-${COMMIT_SHA}" | shasum | awk '{print $1}') + TEST_REPO_NAME="${REPOSITORY_NAME}-${PSEUDO_RAND_SUFFIX}" + echo "::set-output name=test_repo_name::$TEST_REPO_NAME" - name: bootstrap init run: | /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ - --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ + --repository=${{ steps.vars.outputs.test_repo_name }} \ --branch=main \ --path=test-cluster env: @@ -48,7 +54,7 @@ jobs: run: | /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ - --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ + --repository=${{ steps.vars.outputs.test_repo_name }} \ --branch=main \ --path=test-cluster env: @@ -61,19 +67,19 @@ jobs: run: | /tmp/flux bootstrap github --manifests ./manifests/install/ \ --owner=fluxcd-testing \ - --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ + --repository=${{ steps.vars.outputs.test_repo_name }} \ --branch=main \ --path=test-cluster env: GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} - name: delete repository run: | - /tmp/flux bootstrap github --manifests ./manifests/install/ \ - --owner=fluxcd-testing \ - --repository=flux-test-${{ steps.vars.outputs.sha_short }} \ - --branch=main \ - --path=test-cluster \ - --delete + curl \ + -X DELETE \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + --fail --silent \ + https://api.github.com/repos/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }} env: GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} - name: Debug failure diff --git a/.github/workflows/update.yaml b/.github/workflows/update.yaml index abac730f..da459b9f 100644 --- a/.github/workflows/update.yaml +++ b/.github/workflows/update.yaml @@ -30,7 +30,7 @@ jobs: # bump kustomize sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml" - if [[ ! -z $(go list -m all | grep "github.com/fluxcd/$1/api" | awk '{print $2}') ]]; then + if [[ ! -z $(grep "github.com/fluxcd/$1/api" go.mod | awk '{print $2}') ]]; then # bump go mod go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}" fi diff --git a/cmd/flux/bootstrap.go b/cmd/flux/bootstrap.go index ea12e0a4..ba84a91d 100644 --- a/cmd/flux/bootstrap.go +++ b/cmd/flux/bootstrap.go @@ -36,6 +36,7 @@ import ( "github.com/fluxcd/flux2/pkg/manifestgen/install" kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization" "github.com/fluxcd/flux2/pkg/manifestgen/sync" + "github.com/fluxcd/flux2/pkg/status" ) var bootstrapCmd = &cobra.Command{ @@ -176,19 +177,24 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { kubectlArgs := []string{"apply", "-f", manifestPath} if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { - return fmt.Errorf("install failed") + return fmt.Errorf("install failed: %w", err) } - - statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout) + kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + componentRefs, err := buildComponentObjectRefs(components...) if err != nil { return fmt.Errorf("install failed: %w", err) } - logger.Waitingf("verifying installation") - if err := statusChecker.Assess(components...); err != nil { + if err := statusChecker.Assess(componentRefs...); err != nil { return fmt.Errorf("install failed") } - return nil } diff --git a/cmd/flux/bootstrap_github.go b/cmd/flux/bootstrap_github.go index 8fb48ac3..56f62878 100644 --- a/cmd/flux/bootstrap_github.go +++ b/cmd/flux/bootstrap_github.go @@ -80,7 +80,6 @@ type githubFlags struct { hostname string path flags.SafeRelativePath teams []string - delete bool sshHostname string } @@ -101,9 +100,6 @@ func init() { bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one") bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") - bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.delete, "delete", false, "delete repository (used for testing only)") - bootstrapGitHubCmd.Flags().MarkHidden("delete") - bootstrapCmd.AddCommand(bootstrapGitHubCmd) } @@ -163,14 +159,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error { } defer os.RemoveAll(tmpDir) - if githubArgs.delete { - if err := provider.DeleteRepository(ctx, repository); err != nil { - return err - } - logger.Successf("repository deleted") - return nil - } - // create GitHub repository if doesn't exists logger.Actionf("connecting to %s", githubArgs.hostname) changed, err := provider.CreateRepository(ctx, repository) @@ -260,7 +248,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("git URL parse failed: %w", err) } - secretOpts.SSHHostname = u.Hostname() + secretOpts.SSHHostname = u.Host secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm secretOpts.RSAKeyBits = 2048 } diff --git a/cmd/flux/bootstrap_gitlab.go b/cmd/flux/bootstrap_gitlab.go index 15088d31..8acf93aa 100644 --- a/cmd/flux/bootstrap_gitlab.go +++ b/cmd/flux/bootstrap_gitlab.go @@ -235,7 +235,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("git URL parse failed: %w", err) } - secretOpts.SSHHostname = u.Hostname() + secretOpts.SSHHostname = u.Host secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm secretOpts.RSAKeyBits = 2048 } diff --git a/cmd/flux/check.go b/cmd/flux/check.go index 5e1a24f3..da4fca96 100644 --- a/cmd/flux/check.go +++ b/cmd/flux/check.go @@ -34,6 +34,7 @@ import ( "github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/pkg/manifestgen/install" + "github.com/fluxcd/flux2/pkg/status" ) var checkCmd = &cobra.Command{ @@ -205,12 +206,17 @@ func componentsCheck() bool { ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) defer cancel() - kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) + kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) if err != nil { return false } - statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout) + statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger) + if err != nil { + return false + } + + kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) if err != nil { return false } @@ -220,10 +226,10 @@ func componentsCheck() bool { var list v1.DeploymentList if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil { for _, d := range list.Items { - if err := statusChecker.Assess(d.Name); err != nil { - ok = false - } else { - logger.Successf("%s: healthy", d.Name) + if ref, err := buildComponentObjectRefs(d.Name); err == nil { + if err := statusChecker.Assess(ref...); err != nil { + ok = false + } } for _, c := range d.Spec.Template.Spec.Containers { logger.Actionf(c.Image) diff --git a/cmd/flux/create_secret_git.go b/cmd/flux/create_secret_git.go index b84bcbbd..d24faadc 100644 --- a/cmd/flux/create_secret_git.go +++ b/cmd/flux/create_secret_git.go @@ -129,7 +129,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error { } switch u.Scheme { case "ssh": - opts.SSHHostname = u.Hostname() + opts.SSHHostname = u.Host opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm) opts.RSAKeyBits = int(secretGitArgs.rsaBits) opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve diff --git a/cmd/flux/create_source_git.go b/cmd/flux/create_source_git.go index 2ee13daa..e245af6b 100644 --- a/cmd/flux/create_source_git.go +++ b/cmd/flux/create_source_git.go @@ -215,7 +215,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error { } switch u.Scheme { case "ssh": - secretOpts.SSHHostname = u.Hostname() + secretOpts.SSHHostname = u.Host secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm) secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits) secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve diff --git a/cmd/flux/install.go b/cmd/flux/install.go index fb2740e2..fbbc586b 100644 --- a/cmd/flux/install.go +++ b/cmd/flux/install.go @@ -30,6 +30,7 @@ import ( "github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/pkg/manifestgen/install" + "github.com/fluxcd/flux2/pkg/status" ) var installCmd = &cobra.Command{ @@ -200,7 +201,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error { applyOutput = utils.ModeOS } if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { - return fmt.Errorf("install failed") + return fmt.Errorf("install failed: %w", err) } if installArgs.dryRun { @@ -208,13 +209,20 @@ func installCmdRun(cmd *cobra.Command, args []string) error { return nil } - statusChecker, err := NewStatusChecker(time.Second, time.Minute) + kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + componentRefs, err := buildComponentObjectRefs(components...) if err != nil { return fmt.Errorf("install failed: %w", err) } - logger.Waitingf("verifying installation") - if err := statusChecker.Assess(components...); err != nil { + if err := statusChecker.Assess(componentRefs...); err != nil { return fmt.Errorf("install failed") } diff --git a/cmd/flux/logs.go b/cmd/flux/logs.go new file mode 100644 index 00000000..19305728 --- /dev/null +++ b/cmd/flux/logs.go @@ -0,0 +1,262 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "github.com/fluxcd/flux2/internal/flags" + "github.com/fluxcd/flux2/internal/utils" + "github.com/spf13/cobra" + "html/template" + "io" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "os" + "strings" + "sync" +) + +var logsCmd = &cobra.Command{ + Use: "logs", + Short: "Display formatted logs for toolkit components", + Long: "The logs command displays formatted logs from various toolkit components.", + Example: `# Get logs from toolkit components + flux logs + + # Stream logs from toolkit components + flux logs --follow + + # Get logs from toolkit components in a particular namespace + flux logs --flux-namespace my-namespace + + # Get logs for a particular log level + flux logs --level=info + + # Filter logs by kind, name, or namespace + flux logs --kind=kustomization --name podinfo --namespace default + `, + RunE: logsCmdRun, +} + +type logsFlags struct { + logLevel flags.LogLevel + follow bool + tail int64 + kind string + name string + fluxNamespace string + allNamespaces bool +} + +var logsArgs = &logsFlags{ + tail: -1, +} + +func init() { + logsCmd.Flags().Var(&logsArgs.logLevel, "level", logsArgs.logLevel.Description()) + logsCmd.Flags().StringVarP(&logsArgs.kind, "kind", "", logsArgs.kind, "displays errors of a particular toolkit kind e.g GitRepository") + logsCmd.Flags().StringVarP(&logsArgs.name, "name", "", logsArgs.name, "specifies the name of the object logs to be displayed") + logsCmd.Flags().BoolVarP(&logsArgs.follow, "follow", "f", logsArgs.follow, "Specifies if the logs should be streamed") + logsCmd.Flags().Int64VarP(&logsArgs.tail, "tail", "", logsArgs.tail, "lines of recent log file to display") + logsCmd.Flags().StringVarP(&logsArgs.fluxNamespace, "flux-namespace", "", rootArgs.defaults.Namespace, "the namespace where the Flux components are running.") + logsCmd.Flags().BoolVarP(&logsArgs.allNamespaces, "all-namespaces", "A", false, "displays logs for objects across all namespaces") + rootCmd.AddCommand(logsCmd) +} + +func logsCmdRun(cmd *cobra.Command, args []string) error { + fluxSelector := fmt.Sprintf("app.kubernetes.io/instance=%s", logsArgs.fluxNamespace) + + ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) + defer cancel() + + var pods []corev1.Pod + cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) + if err != nil { + return err + } + + clientset, err := kubernetes.NewForConfig(cfg) + if err != nil { + return err + } + + if len(args) > 0 { + return fmt.Errorf("no argument required") + } + + pods, err = getPods(ctx, clientset, fluxSelector) + if err != nil { + return err + } + + logOpts := &corev1.PodLogOptions{ + Follow: logsArgs.follow, + } + + if logsArgs.tail > -1 { + logOpts.TailLines = &logsArgs.tail + } + + var requests []rest.ResponseWrapper + for _, pod := range pods { + req := clientset.CoreV1().Pods(logsArgs.fluxNamespace).GetLogs(pod.Name, logOpts) + requests = append(requests, req) + } + + if logsArgs.follow && len(requests) > 1 { + return parallelPodLogs(ctx, requests) + } + + return podLogs(ctx, requests) +} + +func getPods(ctx context.Context, c *kubernetes.Clientset, label string) ([]corev1.Pod, error) { + var ret []corev1.Pod + + opts := metav1.ListOptions{ + LabelSelector: label, + } + deployList, err := c.AppsV1().Deployments(logsArgs.fluxNamespace).List(ctx, opts) + if err != nil { + return ret, err + } + + for _, deploy := range deployList.Items { + label := deploy.Spec.Template.Labels + opts := metav1.ListOptions{ + LabelSelector: createLabelStringFromMap(label), + } + podList, err := c.CoreV1().Pods(logsArgs.fluxNamespace).List(ctx, opts) + if err != nil { + return ret, err + } + ret = append(ret, podList.Items...) + } + + return ret, nil +} + +func parallelPodLogs(ctx context.Context, requests []rest.ResponseWrapper) error { + reader, writer := io.Pipe() + wg := &sync.WaitGroup{} + wg.Add(len(requests)) + + var mutex = &sync.Mutex{} + + for _, request := range requests { + go func(req rest.ResponseWrapper) { + defer wg.Done() + if err := logRequest(mutex, ctx, req, os.Stdout); err != nil { + writer.CloseWithError(err) + return + } + }(request) + } + + go func() { + wg.Wait() + writer.Close() + }() + + _, err := io.Copy(os.Stdout, reader) + return err +} + +func podLogs(ctx context.Context, requests []rest.ResponseWrapper) error { + mutex := &sync.Mutex{} + for _, req := range requests { + if err := logRequest(mutex, ctx, req, os.Stdout); err != nil { + return err + } + } + + return nil +} + +func createLabelStringFromMap(m map[string]string) string { + var strArr []string + for key, val := range m { + pair := fmt.Sprintf("%v=%v", key, val) + strArr = append(strArr, pair) + } + + return strings.Join(strArr, ",") +} + +func logRequest(mu *sync.Mutex, ctx context.Context, request rest.ResponseWrapper, w io.Writer) error { + stream, err := request.Stream(ctx) + if err != nil { + return err + } + defer stream.Close() + + scanner := bufio.NewScanner(stream) + + const logTmpl = "{{.Timestamp}} {{.Level}} {{.Kind}}{{if .Name}}/{{.Name}}.{{.Namespace}}{{end}} - {{.Message}} {{.Error}}\n" + t, err := template.New("log").Parse(logTmpl) + if err != nil { + return fmt.Errorf("unable to create template, err: %s", err) + } + + for scanner.Scan() { + line := scanner.Text() + if !strings.HasPrefix(line, "{") { + continue + } + var l ControllerLogEntry + if err := json.Unmarshal([]byte(line), &l); err != nil { + logger.Failuref("parse error: %s", err) + break + } + + mu.Lock() + filterPrintLog(t, &l) + mu.Unlock() + } + + return nil +} + +func filterPrintLog(t *template.Template, l *ControllerLogEntry) { + if logsArgs.logLevel != "" && logsArgs.logLevel != l.Level || + logsArgs.kind != "" && strings.ToLower(logsArgs.kind) != strings.ToLower(l.Kind) || + logsArgs.name != "" && strings.ToLower(logsArgs.name) != strings.ToLower(l.Name) || + !logsArgs.allNamespaces && strings.ToLower(rootArgs.namespace) != strings.ToLower(l.Namespace) { + return + } + + err := t.Execute(os.Stdout, l) + if err != nil { + logger.Failuref("log template error: %s", err) + } +} + +type ControllerLogEntry struct { + Timestamp string `json:"ts"` + Level flags.LogLevel `json:"level"` + Message string `json:"msg"` + Error string `json:"error,omitempty"` + Logger string `json:"logger"` + Kind string `json:"reconciler kind,omitempty"` + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` +} diff --git a/cmd/flux/status.go b/cmd/flux/status.go index fab26c2b..631a3e2f 100644 --- a/cmd/flux/status.go +++ b/cmd/flux/status.go @@ -19,26 +19,16 @@ package main import ( "context" "fmt" - "time" - appsv1 "k8s.io/api/apps/v1" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/cli-utils/pkg/kstatus/polling" - "sigs.k8s.io/cli-utils/pkg/kstatus/polling/aggregator" - "sigs.k8s.io/cli-utils/pkg/kstatus/polling/collector" - "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event" - "sigs.k8s.io/cli-utils/pkg/kstatus/status" "sigs.k8s.io/cli-utils/pkg/object" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "github.com/fluxcd/pkg/apis/meta" - - "github.com/fluxcd/flux2/internal/utils" ) // statusable is used to see if a resource is considered ready in the usual way @@ -51,13 +41,6 @@ type statusable interface { GetStatusConditions() *[]metav1.Condition } -type StatusChecker struct { - pollInterval time.Duration - timeout time.Duration - client client.Client - statusPoller *polling.StatusPoller -} - func isReady(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName, object statusable) wait.ConditionFunc { return func() (bool, error) { @@ -83,74 +66,7 @@ func isReady(ctx context.Context, kubeClient client.Client, } } -func NewStatusChecker(pollInterval time.Duration, timeout time.Duration) (*StatusChecker, error) { - kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return nil, err - } - restMapper, err := apiutil.NewDynamicRESTMapper(kubeConfig) - if err != nil { - return nil, err - } - client, err := client.New(kubeConfig, client.Options{Mapper: restMapper}) - if err != nil { - return nil, err - } - - return &StatusChecker{ - pollInterval: pollInterval, - timeout: timeout, - client: client, - statusPoller: polling.NewStatusPoller(client, restMapper), - }, nil -} - -func (sc *StatusChecker) Assess(components ...string) error { - ctx, cancel := context.WithTimeout(context.Background(), sc.timeout) - defer cancel() - - objRefs, err := sc.getObjectRefs(components) - if err != nil { - return err - } - - opts := polling.Options{PollInterval: sc.pollInterval, UseCache: true} - eventsChan := sc.statusPoller.Poll(ctx, objRefs, opts) - - coll := collector.NewResourceStatusCollector(objRefs) - done := coll.ListenWithObserver(eventsChan, collector.ObserverFunc( - func(statusCollector *collector.ResourceStatusCollector, e event.Event) { - var rss []*event.ResourceStatus - for _, rs := range statusCollector.ResourceStatuses { - rss = append(rss, rs) - } - desired := status.CurrentStatus - aggStatus := aggregator.AggregateStatus(rss, desired) - if aggStatus == desired { - cancel() - return - } - }), - ) - <-done - - if coll.Error != nil || ctx.Err() == context.DeadlineExceeded { - for _, rs := range coll.ResourceStatuses { - if rs.Status != status.CurrentStatus { - if !sc.deploymentExists(rs.Identifier) { - logger.Failuref("%s: deployment not found", rs.Identifier.Name) - } else { - logger.Failuref("%s: unhealthy (timed out waiting for rollout)", rs.Identifier.Name) - } - } - } - return fmt.Errorf("timed out waiting for condition") - } - - return nil -} - -func (sc *StatusChecker) getObjectRefs(components []string) ([]object.ObjMetadata, error) { +func buildComponentObjectRefs(components ...string) ([]object.ObjMetadata, error) { var objRefs []object.ObjMetadata for _, deployment := range components { objMeta, err := object.CreateObjMetadata(rootArgs.namespace, deployment, schema.GroupKind{Group: "apps", Kind: "Deployment"}) @@ -161,20 +77,3 @@ func (sc *StatusChecker) getObjectRefs(components []string) ([]object.ObjMetadat } return objRefs, nil } - -func (sc *StatusChecker) objMetadataToString(om object.ObjMetadata) string { - return fmt.Sprintf("%s '%s/%s'", om.GroupKind.Kind, om.Namespace, om.Name) -} - -func (sc *StatusChecker) deploymentExists(om object.ObjMetadata) bool { - ctx, cancel := context.WithTimeout(context.Background(), sc.timeout) - defer cancel() - - namespacedName := types.NamespacedName{ - Namespace: om.Namespace, - Name: om.Name, - } - var existing appsv1.Deployment - err := sc.client.Get(ctx, namespacedName, &existing) - return err == nil -} diff --git a/docs/_static/custom.css b/docs/_static/custom.css index fe31d76d..e17c215b 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -94,4 +94,29 @@ body { .progress-0plus .progress-bar { background-color: #ff1744; - } \ No newline at end of file + } + +/* Custom admonitions */ +/* See https://squidfunk.github.io/mkdocs-material/reference/admonitions */ +:root { + --md-admonition-icon--heart: url('data:image/svg+xml;charset=utf-8,') +} +.md-typeset .admonition.heart, +.md-typeset details.heart { + border-color: rgb(233, 30, 99); +} +.md-typeset .heart > .admonition-title, +.md-typeset .heart > summary { + background-color: rgba(233, 30, 99, 0.1); +} +.md-typeset .heart > .admonition-title::before, +.md-typeset .heart > summary::before { + background-color: rgb(233, 30, 99); + -webkit-mask-image: var(--md-admonition-icon--heart); + mask-image: var(--md-admonition-icon--heart); +} + +.timetable-explicit-col-widths th:nth-child(1) { width: 4%; } +.timetable-explicit-col-widths th:nth-child(2) { width: 32%; } +.timetable-explicit-col-widths th:nth-child(3) { width: 32%; } +.timetable-explicit-col-widths th:nth-child(4) { width: 32%; } diff --git a/docs/cmd/flux.md b/docs/cmd/flux.md index e8642943..36a7c832 100644 --- a/docs/cmd/flux.md +++ b/docs/cmd/flux.md @@ -85,6 +85,7 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way. * [flux export](flux_export.md) - Export resources in YAML format * [flux get](flux_get.md) - Get sources and resources * [flux install](flux_install.md) - Install or upgrade Flux +* [flux logs](flux_logs.md) - Display formatted logs for toolkit components * [flux reconcile](flux_reconcile.md) - Reconcile sources and resources * [flux resume](flux_resume.md) - Resume suspended resources * [flux suspend](flux_suspend.md) - Suspend resources diff --git a/docs/cmd/flux_logs.md b/docs/cmd/flux_logs.md new file mode 100644 index 00000000..ba29d3d6 --- /dev/null +++ b/docs/cmd/flux_logs.md @@ -0,0 +1,59 @@ +## flux logs + +Display formatted logs for toolkit components + +### Synopsis + +The logs command displays formatted logs from various toolkit components. + +``` +flux logs [flags] +``` + +### Examples + +``` +# Get logs from toolkit components + flux logs + + # Stream logs from toolkit components + flux logs --follow + + # Get logs from toolkit components in a particular namespace + flux logs --flux-namespace my-namespace + + # Get logs for a particular log level + flux logs --level=info + + # Filter logs by kind, name, or namespace + flux logs --kind=kustomization --name podinfo --namespace default + +``` + +### Options + +``` + -A, --all-namespaces displays logs for objects across all namespaces + --flux-namespace string the namespace where the Flux components are running. (default "flux-system") + -f, --follow Specifies if the logs should be streamed + -h, --help help for logs + --kind string displays errors of a particular toolkit kind e.g GitRepository + --level logLevel log level, available options are: (debug, info, error) + --name string specifies the name of the object logs to be displayed + --tail int lines of recent log file to display (default -1) +``` + +### Options inherited from parent commands + +``` + --context string kubernetes context to use + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + -n, --namespace string the namespace scope for this operation (default "flux-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines + diff --git a/docs/guides/flux-v1-automation-migration.md b/docs/guides/flux-v1-automation-migration.md index 6f21ce25..80637384 100644 --- a/docs/guides/flux-v1-automation-migration.md +++ b/docs/guides/flux-v1-automation-migration.md @@ -431,8 +431,8 @@ spec: ``` !!! hint - If you are using the same image repository in several manifests, you only need one - `ImageRepository` object for it. + If you are using the same image repository in several manifests, you only need one + `ImageRepository` object for it. ##### Using image registry credentials for scanning diff --git a/docs/migration/timetable.md b/docs/migration/timetable.md new file mode 100644 index 00000000..537028a7 --- /dev/null +++ b/docs/migration/timetable.md @@ -0,0 +1,38 @@ +--- +hide: + # The table data on this page is easier to read when wider + # The TOC right column is already blank anyway + - toc +--- +# Migration and Support Timetable + +!!! heart "Flux Migration Commitment" + This public timetable clarifies our commitment to end users. + Its purpose is to help improve your experience in deciding how and when to plan infra decisions related to Flux versions. + Please refer to the [Roadmap](../roadmap/index.md) for additional details. + + + +
+ + + +| Date | Flux 1 | Flux 2 CLI | GOTK[^1] | +| -- | -- | -- | -- | +| Oct 6, 2020 | ^^[Maintenance Mode](https://github.com/fluxcd/website/pull/25)^^
| ^^Development Mode^^
| ^^All Alpha^^[^2] | +Feb 18, 2021 | ^^Partial Migration Mode^^
| ^^Feature Parity^^ | ^^Image Automation Alpha. All others reached Feature Parity, Beta^^ | +| TBD | ^^Superseded^^
| ^^Needs further testing, may get breaking changes^^
| ^^All Beta, Production Ready^^[^3]
| +| TBD | ^^Migration and security support only^^
| ^^Public release (GA), Production Ready^^
| ^^All Beta, Production Ready^^ | +| TBD | ^^Archived^^
| ^^Continued active development^^ | ^^Continued active development^^ | + + +
+ +[^1]: GOTK is shorthand for the [GitOps Toolkit](https://toolkit.fluxcd.io/components/) APIs and Controllers + +[^2]: Versioning: Flux 2 is a multi-service architecture, so requires a more complex explanation than Flux 1: + - Flux 2 CLI follows [Semantic Versioning](https://semver.org/) scheme + - The GitOps Toolkit APIs follow the [Kubernetes API versioning](https://kubernetes.io/docs/reference/using-api/#api-versioning) pattern. See [Roadmap](https://toolkit.fluxcd.io/roadmap/) for component versions. + - These are coordinated for cross-compatibility: For each Flux 2 CLI tag, CLI and GOTK versions are end-to-end tested together, so you may safely upgrade from one MINOR/PATCH version to another. + +[^3]: The GOTK Custom Resource Definitions which are at `v1beta1` and `v2beta1` and their controllers are considered stable and production ready. Going forward, breaking changes to the beta CRDs will be accompanied by a conversion mechanism. diff --git a/docs/roadmap/index.md b/docs/roadmap/index.md index d4daccf4..b4088451 100644 --- a/docs/roadmap/index.md +++ b/docs/roadmap/index.md @@ -4,6 +4,7 @@ The Flux custom resource definitions which are at `v1beta1` and `v2beta1` and their controllers are considered stable and production ready. Going forward, breaking changes to the beta CRDs will be accompanied by a conversion mechanism. + Please see the [Migration and Suport Timetable](../migration/timetable.md) for our commitment to end users. The following components (included by default in [flux bootstrap](../guides/installation.md#bootstrap)) are considered production ready: diff --git a/go.mod b/go.mod index 2e10d7c0..e004d3e8 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,18 @@ go 1.16 require ( github.com/Masterminds/semver/v3 v3.1.0 github.com/cyphar/filepath-securejoin v0.2.2 - github.com/fluxcd/helm-controller/api v0.8.1 - github.com/fluxcd/image-automation-controller/api v0.6.1 - github.com/fluxcd/image-reflector-controller/api v0.7.0 - github.com/fluxcd/kustomize-controller/api v0.9.1 - github.com/fluxcd/notification-controller/api v0.9.0 + github.com/fluxcd/helm-controller/api v0.8.2 + github.com/fluxcd/image-automation-controller/api v0.7.0 + github.com/fluxcd/image-reflector-controller/api v0.7.1 + github.com/fluxcd/kustomize-controller/api v0.9.3 + github.com/fluxcd/notification-controller/api v0.10.0 github.com/fluxcd/pkg/apis/meta v0.8.0 github.com/fluxcd/pkg/git v0.3.0 - github.com/fluxcd/pkg/runtime v0.8.3 + github.com/fluxcd/pkg/runtime v0.8.5 github.com/fluxcd/pkg/ssh v0.0.5 github.com/fluxcd/pkg/untar v0.0.5 github.com/fluxcd/pkg/version v0.0.1 - github.com/fluxcd/source-controller/api v0.9.0 + github.com/fluxcd/source-controller/api v0.9.1 github.com/google/go-containerregistry v0.2.0 github.com/manifoldco/promptui v0.7.0 github.com/olekukonko/tablewriter v0.0.4 @@ -29,7 +29,7 @@ require ( k8s.io/cli-runtime v0.20.2 // indirect k8s.io/client-go v0.20.2 sigs.k8s.io/cli-utils v0.22.2 - sigs.k8s.io/controller-runtime v0.8.2 + sigs.k8s.io/controller-runtime v0.8.3 sigs.k8s.io/kustomize/api v0.7.4 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 95050edb..11bb7267 100644 --- a/go.sum +++ b/go.sum @@ -188,33 +188,33 @@ github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fluxcd/helm-controller/api v0.8.1 h1:AEmw/xaRi2y9lD6TB+2w1Govj7OhD5oxoDx6HGty1yM= -github.com/fluxcd/helm-controller/api v0.8.1/go.mod h1:cFceNc/mOBa+Qi3NE8NDY2w3FAEectauTm8c10mcBis= -github.com/fluxcd/image-automation-controller/api v0.6.1 h1:LgjjNYXrVojfgjit37GA0SDYre3AaDabYzT7L6lWO7I= -github.com/fluxcd/image-automation-controller/api v0.6.1/go.mod h1:8Q/baOONPrSJFMq7+zxp/t2WGrqVFRUx4HnTrg37pNE= -github.com/fluxcd/image-reflector-controller/api v0.7.0 h1:Mlu9ybrL+MtWcaIex4+FOcYuk+0vA/7bYj8MxVvLR1A= -github.com/fluxcd/image-reflector-controller/api v0.7.0/go.mod h1:KHWknF2xu/GZ4uLSQcAmfONZYjsbwNqyk3OvMQTmMsA= -github.com/fluxcd/kustomize-controller/api v0.9.1 h1:o6cxtLiXUdEeTJMxoeoLdld7hsgf7L4mNO6+i2MD2U8= -github.com/fluxcd/kustomize-controller/api v0.9.1/go.mod h1:VhUwaSsgrXVgO8Qcx6ZO0isqb5TpDgbyyitCeXYqSM4= -github.com/fluxcd/notification-controller/api v0.9.0 h1:aEIZu01EHlDH7I8/TyOkvMknlDV8NBNhuZ9cMQ8Kp0Q= -github.com/fluxcd/notification-controller/api v0.9.0/go.mod h1:nJqSGiecNkJLxuO2KWMu5YUTLaYT/A57854FMm4oX9Q= +github.com/fluxcd/helm-controller/api v0.8.2 h1:ELSC6dal01jtzn8B6ffArNklbul9/k9lpEd6msefVAE= +github.com/fluxcd/helm-controller/api v0.8.2/go.mod h1:WDVuo3g6n3eZy8l5U/Zqo0aL+LcFV1C/HoNAUzWLtzU= +github.com/fluxcd/image-automation-controller/api v0.7.0 h1:mLaELYT52/FpZ93Mr+QMSK8UT0OBVQT4oA9kxO8NiEk= +github.com/fluxcd/image-automation-controller/api v0.7.0/go.mod h1:7E2dCvoxmTkDttp+Hk8v9eoSK/CdFmFhRKInEXC3yVY= +github.com/fluxcd/image-reflector-controller/api v0.7.1 h1:Cng36D1J25WYZ0ZB6dwzDtGR9MIyIcSUMYxHpb0IYXA= +github.com/fluxcd/image-reflector-controller/api v0.7.1/go.mod h1:J18L71jiHYrAu2dg0tgOkOjP+GtQldC1oslhTeX0jqc= +github.com/fluxcd/kustomize-controller/api v0.9.3 h1:VbeU97pmx3vmgverqZIRyyBm1IKyPBZZAIo7mc3fZ+8= +github.com/fluxcd/kustomize-controller/api v0.9.3/go.mod h1:MDTwohIXqbId3qbhVNF7lAYLSBMzxq5MHINFN4bqDRs= +github.com/fluxcd/notification-controller/api v0.10.0 h1:7mxeBPnFzpL4Z+X5YiytQRzaDKrryb6TIQkC9ASJO28= +github.com/fluxcd/notification-controller/api v0.10.0/go.mod h1:CA02ixmq+kFN9eBkruvJClkMqffgRjYBMxym3AfhO6c= github.com/fluxcd/pkg/apis/kustomize v0.0.1 h1:TkA80R0GopRY27VJqzKyS6ifiKIAfwBd7OHXtV3t2CI= github.com/fluxcd/pkg/apis/kustomize v0.0.1/go.mod h1:JAFPfnRmcrAoG1gNiA8kmEXsnOBuDyZ/F5X4DAQcVV0= -github.com/fluxcd/pkg/apis/meta v0.7.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po= github.com/fluxcd/pkg/apis/meta v0.8.0 h1:wqWpUsxhKHB1ZztcvOz+vnyhdKW9cWmjFp8Vci/XOdk= github.com/fluxcd/pkg/apis/meta v0.8.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po= github.com/fluxcd/pkg/git v0.3.0 h1:nrKZWZ/ymDevud3Wf1LEieO/QcNPnqz1/MrkZBFcg9o= github.com/fluxcd/pkg/git v0.3.0/go.mod h1:ZwG0iLOqNSyNw6lsPIAO+v6+BqqCXyV+r1Oq6Lm+slg= -github.com/fluxcd/pkg/runtime v0.8.3 h1:Zjk4fyAfBdBQ4GTokjisab7KyHHczCqKSpJi8+oVrNw= -github.com/fluxcd/pkg/runtime v0.8.3/go.mod h1:AM/hMD0mKtRqhKPU7NGDzm+3UXPpdnX8oBlcxLt11AY= +github.com/fluxcd/pkg/runtime v0.8.4/go.mod h1:JD0eZIn5xkTeHHQUWXSqJPIh/ecO0d0qrUKbSVHnpnw= +github.com/fluxcd/pkg/runtime v0.8.5 h1:ynh8fszbLQ3QSisQBNOABEUTnvt+/QfCdaL6gOJQcoQ= +github.com/fluxcd/pkg/runtime v0.8.5/go.mod h1:JD0eZIn5xkTeHHQUWXSqJPIh/ecO0d0qrUKbSVHnpnw= github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A= github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs= github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk= github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw= github.com/fluxcd/pkg/version v0.0.1 h1:/8asQoDXSThz3csiwi4Qo8Zb6blAxLXbtxNgeMJ9bCg= github.com/fluxcd/pkg/version v0.0.1/go.mod h1:WAF4FEEA9xyhngF8TDxg3UPu5fA1qhEYV8Pmi2Il01Q= -github.com/fluxcd/source-controller/api v0.9.0 h1:ohV8AvmvkUK0N7+YKPIOlMSLaNG0SpFcNLtlmW18xuM= -github.com/fluxcd/source-controller/api v0.9.0/go.mod h1:68+cPuz1G45f0SDRwEfTL419011ffveLIDA9nssLlkU= +github.com/fluxcd/source-controller/api v0.9.1 h1:kaL+tBflccsuj3NDESPPQyKXlZXlAgyNoT2nYY02JAE= +github.com/fluxcd/source-controller/api v0.9.1/go.mod h1:Vuw+7UqEUUOdkKBfTUPHwaQgbn6LL2FwqPDx2UAk7NE= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -1205,8 +1205,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/cli-utils v0.22.2 h1:xPD02b++uK990/dAg/rM0LKDOb2sTWZPI1v8IZPfCn0= sigs.k8s.io/cli-utils v0.22.2/go.mod h1:unl8itcwGPqo41QSyksbXTWFbfMqap1o/4oiUxPnQfw= sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo= -sigs.k8s.io/controller-runtime v0.8.2 h1:SBWmI0b3uzMIUD/BIXWNegrCeZmPJ503pOtwxY0LPHM= -sigs.k8s.io/controller-runtime v0.8.2/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= +sigs.k8s.io/controller-runtime v0.8.3 h1:GMHvzjTmaWHQB8HadW+dIvBoJuLvZObYJ5YoZruPRao= +sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= sigs.k8s.io/kind v0.9.0/go.mod h1:cxKQWwmbtRDzQ+RNKnR6gZG6fjbeTtItp5cGf+ww+1Y= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= diff --git a/manifests/bases/helm-controller/kustomization.yaml b/manifests/bases/helm-controller/kustomization.yaml index 96931696..788742f2 100644 --- a/manifests/bases/helm-controller/kustomization.yaml +++ b/manifests/bases/helm-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/helm-controller/releases/download/v0.8.1/helm-controller.crds.yaml -- https://github.com/fluxcd/helm-controller/releases/download/v0.8.1/helm-controller.deployment.yaml +- https://github.com/fluxcd/helm-controller/releases/download/v0.8.2/helm-controller.crds.yaml +- https://github.com/fluxcd/helm-controller/releases/download/v0.8.2/helm-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/manifests/bases/image-automation-controller/kustomization.yaml b/manifests/bases/image-automation-controller/kustomization.yaml index e58dbe1f..3e8147b8 100644 --- a/manifests/bases/image-automation-controller/kustomization.yaml +++ b/manifests/bases/image-automation-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/image-automation-controller/releases/download/v0.6.1/image-automation-controller.crds.yaml -- https://github.com/fluxcd/image-automation-controller/releases/download/v0.6.1/image-automation-controller.deployment.yaml +- https://github.com/fluxcd/image-automation-controller/releases/download/v0.7.0/image-automation-controller.crds.yaml +- https://github.com/fluxcd/image-automation-controller/releases/download/v0.7.0/image-automation-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/manifests/bases/image-reflector-controller/kustomization.yaml b/manifests/bases/image-reflector-controller/kustomization.yaml index 0e72106c..c5648337 100644 --- a/manifests/bases/image-reflector-controller/kustomization.yaml +++ b/manifests/bases/image-reflector-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.7.0/image-reflector-controller.crds.yaml -- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.7.0/image-reflector-controller.deployment.yaml +- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.7.1/image-reflector-controller.crds.yaml +- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.7.1/image-reflector-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/manifests/bases/kustomize-controller/kustomization.yaml b/manifests/bases/kustomize-controller/kustomization.yaml index 382a0baf..748a626a 100644 --- a/manifests/bases/kustomize-controller/kustomization.yaml +++ b/manifests/bases/kustomize-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/kustomize-controller/releases/download/v0.9.2/kustomize-controller.crds.yaml -- https://github.com/fluxcd/kustomize-controller/releases/download/v0.9.2/kustomize-controller.deployment.yaml +- https://github.com/fluxcd/kustomize-controller/releases/download/v0.9.3/kustomize-controller.crds.yaml +- https://github.com/fluxcd/kustomize-controller/releases/download/v0.9.3/kustomize-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/manifests/bases/notification-controller/kustomization.yaml b/manifests/bases/notification-controller/kustomization.yaml index 0946d5ef..36e4df72 100644 --- a/manifests/bases/notification-controller/kustomization.yaml +++ b/manifests/bases/notification-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/notification-controller/releases/download/v0.9.0/notification-controller.crds.yaml -- https://github.com/fluxcd/notification-controller/releases/download/v0.9.0/notification-controller.deployment.yaml +- https://github.com/fluxcd/notification-controller/releases/download/v0.10.0/notification-controller.crds.yaml +- https://github.com/fluxcd/notification-controller/releases/download/v0.10.0/notification-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/manifests/bases/source-controller/kustomization.yaml b/manifests/bases/source-controller/kustomization.yaml index 84af419d..fe6c3b69 100644 --- a/manifests/bases/source-controller/kustomization.yaml +++ b/manifests/bases/source-controller/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- https://github.com/fluxcd/source-controller/releases/download/v0.9.0/source-controller.crds.yaml -- https://github.com/fluxcd/source-controller/releases/download/v0.9.0/source-controller.deployment.yaml +- https://github.com/fluxcd/source-controller/releases/download/v0.9.1/source-controller.crds.yaml +- https://github.com/fluxcd/source-controller/releases/download/v0.9.1/source-controller.deployment.yaml - account.yaml patchesJson6902: - target: diff --git a/mkdocs.yml b/mkdocs.yml index 5cbefe28..ef5e6bba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,31 +26,34 @@ plugins: markdown_extensions: - admonition - - meta - codehilite: guess_lang: false - - toc: - permalink: true + - footnotes + - meta + - pymdownx.caret + - pymdownx.emoji: + emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:materialx.emoji.twemoji + - pymdownx.extra + - pymdownx.progressbar - pymdownx.superfences: highlight_code: true - pymdownx.tabbed - - pymdownx.tilde - - pymdownx.progressbar - pymdownx.tasklist - - pymdownx.superfences - - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.tilde + - toc: + permalink: true nav: - Introduction: index.md - Core Concepts: core-concepts/index.md - Get Started: get-started/index.md - Migration: - - Migrate from Flux v1: guides/flux-v1-migration.md - - Migrate from Flux v1 image update automation: guides/flux-v1-automation-migration.md - - Migrate from the Helm Operator: guides/helm-operator-migration.md - - FAQ: guides/faq-migration.md + - Migration and Support Timetable: migration/timetable.md + - Migrate from Flux v1: guides/flux-v1-migration.md + - Migrate from Flux v1 image update automation: guides/flux-v1-automation-migration.md + - Migrate from the Helm Operator: guides/helm-operator-migration.md + - FAQ: guides/faq-migration.md - Guides: - Installation: guides/installation.md - Manage Helm Releases: guides/helmreleases.md @@ -158,6 +161,7 @@ nav: - Get images repository: cmd/flux_get_images_repository.md - Get images update: cmd/flux_get_images_update.md - Install: cmd/flux_install.md + - Logs: cmd/flux_logs.md - Resume: cmd/flux_resume.md - Resume kustomization: cmd/flux_resume_kustomization.md - Resume helmrelease: cmd/flux_resume_helmrelease.md diff --git a/pkg/log/log.go b/pkg/log/log.go index c9ed2128..2833e077 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -22,7 +22,7 @@ type Logger interface { Generatef(format string, a ...interface{}) // Waitingf logs a formatted waiting message. Waitingf(format string, a ...interface{}) - // Waitingf logs a formatted success message. + // Successf logs a formatted success message. Successf(format string, a ...interface{}) // Failuref logs a formatted failure message. Failuref(format string, a ...interface{}) diff --git a/pkg/status/status.go b/pkg/status/status.go new file mode 100644 index 00000000..1a5abc37 --- /dev/null +++ b/pkg/status/status.go @@ -0,0 +1,109 @@ +/* +Copyright 2020, 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package status + +import ( + "context" + "fmt" + "strings" + "time" + + "k8s.io/client-go/rest" + "sigs.k8s.io/cli-utils/pkg/kstatus/polling" + "sigs.k8s.io/cli-utils/pkg/kstatus/polling/aggregator" + "sigs.k8s.io/cli-utils/pkg/kstatus/polling/collector" + "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event" + "sigs.k8s.io/cli-utils/pkg/kstatus/status" + "sigs.k8s.io/cli-utils/pkg/object" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + "github.com/fluxcd/flux2/pkg/log" +) + +type StatusChecker struct { + pollInterval time.Duration + timeout time.Duration + client client.Client + statusPoller *polling.StatusPoller + logger log.Logger +} + +func NewStatusChecker(kubeConfig *rest.Config, pollInterval time.Duration, timeout time.Duration, log log.Logger) (*StatusChecker, error) { + restMapper, err := apiutil.NewDynamicRESTMapper(kubeConfig) + if err != nil { + return nil, err + } + c, err := client.New(kubeConfig, client.Options{Mapper: restMapper}) + if err != nil { + return nil, err + } + + return &StatusChecker{ + pollInterval: pollInterval, + timeout: timeout, + client: c, + statusPoller: polling.NewStatusPoller(c, restMapper), + logger: log, + }, nil +} + +func (sc *StatusChecker) Assess(identifiers ...object.ObjMetadata) error { + ctx, cancel := context.WithTimeout(context.Background(), sc.timeout) + defer cancel() + + opts := polling.Options{PollInterval: sc.pollInterval, UseCache: true} + eventsChan := sc.statusPoller.Poll(ctx, identifiers, opts) + + coll := collector.NewResourceStatusCollector(identifiers) + done := coll.ListenWithObserver(eventsChan, desiredStatusNotifierFunc(cancel, status.CurrentStatus)) + + <-done + + for _, rs := range coll.ResourceStatuses { + switch rs.Status { + case status.CurrentStatus: + sc.logger.Successf("%s: %s ready", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind)) + case status.NotFoundStatus: + sc.logger.Failuref("%s: %s not found", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind)) + default: + sc.logger.Failuref("%s: %s not ready", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind)) + } + } + + if coll.Error != nil || ctx.Err() == context.DeadlineExceeded { + return fmt.Errorf("timed out waiting for condition") + } + return nil +} + +// desiredStatusNotifierFunc returns an Observer function for the +// ResourceStatusCollector that will cancel the context (using the cancelFunc) +// when all resources have reached the desired status. +func desiredStatusNotifierFunc(cancelFunc context.CancelFunc, + desired status.Status) collector.ObserverFunc { + return func(rsc *collector.ResourceStatusCollector, _ event.Event) { + var rss []*event.ResourceStatus + for _, rs := range rsc.ResourceStatuses { + rss = append(rss, rs) + } + aggStatus := aggregator.AggregateStatus(rss, desired) + if aggStatus == desired { + cancelFunc() + } + } +}