From 76baee3822640a7328f78159e7c18004bec7feb7 Mon Sep 17 00:00:00 2001 From: AsmaaNabilBakr Date: Sun, 16 Jul 2023 10:43:52 +0300 Subject: [PATCH 01/23] fix duplicate icons --- ui/components/Icon.tsx | 5 ++-- ui/components/NavIcons/PoliciesIcon.tsx | 36 +++++++++---------------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index 16f405dcfd..709c75070b 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -22,7 +22,6 @@ import NavigateNextIcon from "@material-ui/icons/NavigateNext"; import PauseIcon from "@material-ui/icons/Pause"; import PersonIcon from "@material-ui/icons/Person"; import PlayIcon from "@material-ui/icons/PlayArrow"; -import Policy from "@material-ui/icons/Policy"; import Remove from "@material-ui/icons/Remove"; import RemoveCircleIcon from "@material-ui/icons/RemoveCircle"; import SaveAltIcon from "@material-ui/icons/SaveAlt"; @@ -255,7 +254,7 @@ function getIcon(i: IconType) { return NotificationsIcon; case IconType.PoliciesIcon: - return PoliciesIcon; + return () => ; case IconType.PolicyConfigsIcon: return PolicyConfigsIcon; @@ -264,7 +263,7 @@ function getIcon(i: IconType) { return VerifiedUser; case IconType.Policy: - return Policy; + return () => ; case IconType.SecretsIcon: return SecretsIcon; diff --git a/ui/components/NavIcons/PoliciesIcon.tsx b/ui/components/NavIcons/PoliciesIcon.tsx index 4461964d62..550bfc29c4 100644 --- a/ui/components/NavIcons/PoliciesIcon.tsx +++ b/ui/components/NavIcons/PoliciesIcon.tsx @@ -1,33 +1,23 @@ import * as React from "react"; - -function PoliciesIcon() { +import { useTheme } from "styled-components"; +function PoliciesIcon({ filled }) { + const theme = useTheme(); return ( - - - - - - - + ); } From 084e6705c68a7c335eb62aae67ebd4b12a8c1856 Mon Sep 17 00:00:00 2001 From: Joshua Israel Date: Tue, 18 Jul 2023 13:10:53 -0400 Subject: [PATCH 02/23] policy icon size --- ui/components/Policies/Utils/PolicyMode.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/Policies/Utils/PolicyMode.tsx b/ui/components/Policies/Utils/PolicyMode.tsx index 02212ec61e..2682cb05e5 100644 --- a/ui/components/Policies/Utils/PolicyMode.tsx +++ b/ui/components/Policies/Utils/PolicyMode.tsx @@ -17,14 +17,14 @@ const PolicyMode = ({ modeName, showName = false }: Props) => { case "audit": mode = { name: "audit", - icon: , + icon: , }; break; case "admission": mode = { name: "enforce", icon: ( - + ), }; break; From ebd641ee1c7e9cce7b2499cf4d996051b660b84f Mon Sep 17 00:00:00 2001 From: AsmaaNabilBakr Date: Mon, 24 Jul 2023 10:53:28 +0300 Subject: [PATCH 03/23] fix audit icon size --- ui/components/Policies/Utils/PolicyMode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/Policies/Utils/PolicyMode.tsx b/ui/components/Policies/Utils/PolicyMode.tsx index 2682cb05e5..0d7c9ac747 100644 --- a/ui/components/Policies/Utils/PolicyMode.tsx +++ b/ui/components/Policies/Utils/PolicyMode.tsx @@ -17,7 +17,7 @@ const PolicyMode = ({ modeName, showName = false }: Props) => { case "audit": mode = { name: "audit", - icon: , + icon: , }; break; case "admission": From e69e26660880614601f32078931716357ec52c0a Mon Sep 17 00:00:00 2001 From: AsmaaNabilBakr Date: Tue, 31 Oct 2023 10:36:24 +0200 Subject: [PATCH 04/23] update icon --- ui/components/NavIcons/PoliciesIcon.tsx | 32 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/ui/components/NavIcons/PoliciesIcon.tsx b/ui/components/NavIcons/PoliciesIcon.tsx index 550bfc29c4..f3952bfb59 100644 --- a/ui/components/NavIcons/PoliciesIcon.tsx +++ b/ui/components/NavIcons/PoliciesIcon.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { useTheme } from "styled-components"; function PoliciesIcon({ filled }) { const theme = useTheme(); + return ( - + + + + + + + + ); } From 3de63c44b9b432a583dff7a674e92301e78bfadb Mon Sep 17 00:00:00 2001 From: Balazs Nadasdi Date: Fri, 27 Oct 2023 11:47:07 +0200 Subject: [PATCH 05/23] refactor: refactor away from deprecated wait.Poll calls With the update, some other calls had to be updated: - `NewDiscoveryRESTMapper` expects an extra `HTTPClient` argument - `client` does not have `NewDelegatingClient` anymore, instead we can create the same resource with `client.New(...)` Resolves #3812 References: - https://github.com/weaveworks/weave-gitops/issues/3812 - https://github.com/kubernetes-sigs/controller-runtime/pull/2150 - https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.15.0 Signed-off-by: Balazs Nadasdi --- core/clustersmngr/cluster/delegating_cache.go | 25 +++++++++++-------- core/clustersmngr/cluster/single.go | 7 +++++- go.mod | 11 -------- go.sum | 21 ++++++++-------- pkg/run/install/install_dashboard.go | 9 +++---- pkg/run/install/install_fluent_bit.go | 6 ++--- pkg/run/install/install_vcluster.go | 6 ++--- pkg/run/session/connect/connect.go | 3 +-- pkg/run/session/remove.go | 6 ++--- pkg/run/watch/setup_dev_ks.go | 12 +++------ 10 files changed, 47 insertions(+), 59 deletions(-) diff --git a/core/clustersmngr/cluster/delegating_cache.go b/core/clustersmngr/cluster/delegating_cache.go index 4060cb68d8..8c6562a32d 100644 --- a/core/clustersmngr/cluster/delegating_cache.go +++ b/core/clustersmngr/cluster/delegating_cache.go @@ -41,7 +41,12 @@ func (c *delegatingCacheCluster) GetHost() string { } func (c *delegatingCacheCluster) makeCachingClient(leafClient client.Client) (client.Client, error) { - mapper, err := apiutil.NewDiscoveryRESTMapper(c.restConfig) + httpClient, err := rest.HTTPClientFor(c.restConfig) + if err != nil { + return nil, fmt.Errorf("could not create HTTP client from config: %w", err) + } + + mapper, err := apiutil.NewDiscoveryRESTMapper(c.restConfig, httpClient) if err != nil { return nil, fmt.Errorf("could not create RESTMapper from config: %w", err) } @@ -58,16 +63,16 @@ func (c *delegatingCacheCluster) makeCachingClient(leafClient client.Client) (cl // https://github.com/kubernetes-sigs/controller-runtime/pull/2150 delegatingCache := newDelegatingCache(leafClient, cache, c.scheme) - delegatingClient, err := client.NewDelegatingClient(client.NewDelegatingClientInput{ - CacheReader: delegatingCache, - Client: leafClient, - // Non-exact field matches are not supported by the cache. - // https://github.com/kubernetes-sigs/controller-runtime/issues/612 - // TODO: Research if we can change the way we query those events so we can enable the cache for it. - UncachedObjects: []client.Object{&v1.Event{}}, - CacheUnstructured: true, + delegatingClient, err := client.New(c.restConfig, client.Options{ + Cache: &client.CacheOptions{ + Reader: delegatingCache, + // Non-exact field matches are not supported by the cache. + // https://github.com/kubernetes-sigs/controller-runtime/issues/612 + // TODO: Research if we can change the way we query those events so we can enable the cache for it. + DisableFor: []client.Object{&v1.Event{}}, + Unstructured: true, + }, }) - if err != nil { return nil, fmt.Errorf("failed creating DelegatingClient: %w", err) } diff --git a/core/clustersmngr/cluster/single.go b/core/clustersmngr/cluster/single.go index 16721cfbad..7e34d7c3d0 100644 --- a/core/clustersmngr/cluster/single.go +++ b/core/clustersmngr/cluster/single.go @@ -55,7 +55,12 @@ func (c *singleCluster) GetHost() string { } func getClientFromConfig(config *rest.Config, scheme *apiruntime.Scheme) (client.Client, error) { - mapper, err := apiutil.NewDiscoveryRESTMapper(config) + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return nil, fmt.Errorf("could not create HTTP client from config: %w", err) + } + + mapper, err := apiutil.NewDiscoveryRESTMapper(config, httpClient) if err != nil { return nil, fmt.Errorf("could not create RESTMapper from config: %w", err) } diff --git a/go.mod b/go.mod index 0988d450b4..98dc732dd0 100644 --- a/go.mod +++ b/go.mod @@ -231,14 +231,3 @@ require ( // Use patched version that fixed recursive gets, and force delete for buckets replace github.com/johannesboyne/gofakes3 => github.com/chanwit/gofakes3 v0.0.0-20220715114300-3f51f1961f7b - -// Replace k8s.io packages v0.26 to downgrade controller-runtime to v0.14.6 -replace ( - k8s.io/api => k8s.io/api v0.26.8 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.8 - k8s.io/apimachinery => k8s.io/apimachinery v0.26.8 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.26.8 - k8s.io/client-go => k8s.io/client-go v0.26.8 - - sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.14.6 -) diff --git a/go.sum b/go.sum index a9226a3a5b..1880459cd6 100644 --- a/go.sum +++ b/go.sum @@ -143,7 +143,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -1061,16 +1060,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.8 h1:k2OtFmQPWfDUyAuYAwQPftVygF/vz4BMGSKnd15iddM= -k8s.io/api v0.26.8/go.mod h1:QaflR7cmG3V9lIz0VLBM+ylndNN897OAUAoJDcgwiQw= -k8s.io/apiextensions-apiserver v0.26.8 h1:ESVQ22MH6YfcpflpZMIvkgnHs/EwOgKKSCkS9AfxJOY= -k8s.io/apiextensions-apiserver v0.26.8/go.mod h1:ySo6rPc9ulNtKoZczw7ljCAdZN3DbyxLNat8wuYk4r8= -k8s.io/apimachinery v0.26.8 h1:SzpGtRX3/j/Ylg8Eg65Iobpxi9Jz4vOvI0qcBZyPVrM= -k8s.io/apimachinery v0.26.8/go.mod h1:qYzLkrQ9lhrZRh0jNKo2cfvf/R1/kQONnSiyB7NUJU0= +k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= +k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= +k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= +k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= +k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= +k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/cli-runtime v0.26.8 h1:LFiS+z20j8gt9Iyo4EsbivzrDYPRbFFj8wmpwdhy7cQ= k8s.io/cli-runtime v0.26.8/go.mod h1:j3YQ0OtQnqsQRsMWbmZrKqbOvN2OUu0K+dPffeKPVj0= -k8s.io/client-go v0.26.8 h1:pPuTYaVtLlg/7n6rqs3MsKLi4XgNaJ3rTMyS37Y5CKU= -k8s.io/client-go v0.26.8/go.mod h1:1sBQqKmdy9rWZYQnoedpc0gnRXG7kU3HrKZvBe2QbGM= +k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= +k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= k8s.io/component-base v0.27.3 h1:g078YmdcdTfrCE4fFobt7qmVXwS8J/3cI1XxRi/2+6k= k8s.io/component-base v0.27.3/go.mod h1:JNiKYcGImpQ44iwSYs6dysxzR9SxIIgQalk4HaCNVUY= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= @@ -1087,8 +1086,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= -sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= -sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= 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.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= diff --git a/pkg/run/install/install_dashboard.go b/pkg/run/install/install_dashboard.go index 28fe5f9096..b235c00436 100644 --- a/pkg/run/install/install_dashboard.go +++ b/pkg/run/install/install_dashboard.go @@ -228,8 +228,7 @@ func ReconcileDashboard(ctx context.Context, kubeClient client.Client, dashboard var sourceRequestedAt string - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { var err error sourceRequestedAt, err = run.RequestReconciliation(ctx, kubeClient, namespacedName, gvk) @@ -240,8 +239,7 @@ func ReconcileDashboard(ctx context.Context, kubeClient client.Client, dashboard } // wait for the reconciliation of dashboard to be done - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { dashboard := &sourcev1b2.HelmChart{} if err := kubeClient.Get(ctx, types.NamespacedName{ Namespace: namespace, @@ -256,8 +254,7 @@ func ReconcileDashboard(ctx context.Context, kubeClient client.Client, dashboard } // wait for dashboard to be ready - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { namespacedName := types.NamespacedName{Namespace: namespace, Name: podName} var labels map[string]string = nil diff --git a/pkg/run/install/install_fluent_bit.go b/pkg/run/install/install_fluent_bit.go index e8505e9178..7126c3bfd9 100644 --- a/pkg/run/install/install_fluent_bit.go +++ b/pkg/run/install/install_fluent_bit.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" "html/template" "strings" "time" + sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" + helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" "github.com/weaveworks/weave-gitops/pkg/logger" "github.com/weaveworks/weave-gitops/pkg/run/constants" @@ -270,8 +271,7 @@ func InstallFluentBit(ctx context.Context, log logger.Logger, kubeClient client. log.Actionf("waiting for HelmRelease %s/%s to be ready", helmRelease.Namespace, helmRelease.Name) - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { instance := appsv1.DaemonSet{} if err := kubeClient.Get( ctx, diff --git a/pkg/run/install/install_vcluster.go b/pkg/run/install/install_vcluster.go index 6019bb40da..df42ef86df 100644 --- a/pkg/run/install/install_vcluster.go +++ b/pkg/run/install/install_vcluster.go @@ -3,12 +3,13 @@ package install import ( "context" "fmt" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" "os" "path/filepath" "strings" "time" + sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" + helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" "github.com/weaveworks/weave-gitops/cmd/gitops/version" coretypes "github.com/weaveworks/weave-gitops/core/server/types" @@ -133,8 +134,7 @@ func installVCluster(kubeClient client.Client, name, namespace, fluxNamespace st } } - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { instance := appsv1.StatefulSet{} if err := kubeClient.Get( context.Background(), diff --git a/pkg/run/session/connect/connect.go b/pkg/run/session/connect/connect.go index cdf5385916..f2ac4bd046 100644 --- a/pkg/run/session/connect/connect.go +++ b/pkg/run/session/connect/connect.go @@ -607,8 +607,7 @@ func (conn *Connection) createServiceAccountToken(vKubeConfig api.Config) (strin token := "" conn.Log.Actionf("Create service account token for %s/%s", serviceAccountNamespace, serviceAccount) - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - err = wait.Poll(time.Second, time.Minute*3, func() (bool, error) { + err = wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { // check if namespace exists _, err := vKubeClient.CoreV1().Namespaces().Get(context.TODO(), serviceAccountNamespace, metav1.GetOptions{}) if err != nil { diff --git a/pkg/run/session/remove.go b/pkg/run/session/remove.go index 972172ba45..67b1c1bfce 100644 --- a/pkg/run/session/remove.go +++ b/pkg/run/session/remove.go @@ -42,8 +42,7 @@ func Remove(kubeClient client.Client, session *InternalSession) error { result = multierror.Append(result, err) } - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { instance := appsv1.StatefulSet{} if err := kubeClient.Get( context.Background(), @@ -62,8 +61,7 @@ func Remove(kubeClient client.Client, session *InternalSession) error { result = multierror.Append(result, err) } - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { pvc := corev1.PersistentVolumeClaim{} if err := kubeClient.Get( context.Background(), diff --git a/pkg/run/watch/setup_dev_ks.go b/pkg/run/watch/setup_dev_ks.go index 9b9c497c10..8310c039c2 100644 --- a/pkg/run/watch/setup_dev_ks.go +++ b/pkg/run/watch/setup_dev_ks.go @@ -429,8 +429,7 @@ func ReconcileDevBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeC } // wait for the reconciliation of dev-bucket to be done - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { devBucket := &sourcev1b2.Bucket{} if err := kubeClient.Get(ctx, types.NamespacedName{ Name: constants.RunDevBucketName, @@ -445,8 +444,7 @@ func ReconcileDevBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeC } // wait for devBucket to be ready - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { devBucket := &sourcev1b2.Bucket{} if err := kubeClient.Get(ctx, types.NamespacedName{ Name: constants.RunDevBucketName, @@ -473,8 +471,7 @@ func ReconcileDevBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeC return err } - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if err := wait.Poll(interval, timeout, func() (bool, error) { + if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { devKs := &kustomizev1.Kustomization{} if err := kubeClient.Get(ctx, types.NamespacedName{ Name: constants.RunDevKsName, @@ -489,8 +486,7 @@ func ReconcileDevBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeC } devKs := &kustomizev1.Kustomization{} - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - devKsErr := wait.Poll(interval, timeout, func() (bool, error) { + devKsErr := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { if err := kubeClient.Get(ctx, types.NamespacedName{ Name: constants.RunDevKsName, Namespace: namespace, From ae13179c88358c4c78e3668373267440fc108e30 Mon Sep 17 00:00:00 2001 From: Balazs Nadasdi Date: Fri, 27 Oct 2023 12:17:29 +0200 Subject: [PATCH 06/23] try to update github.com/fluxcd/pkg/runtime to the earliest version with updated controller-runtime Signed-off-by: Balazs Nadasdi --- go.mod | 33 ++++++++++++++-------------- go.sum | 69 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 98dc732dd0..7790fa7a11 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/fluxcd/image-reflector-controller/api v0.27.2 github.com/fluxcd/kustomize-controller/api v1.0.0 github.com/fluxcd/notification-controller/api v1.0.0 - github.com/fluxcd/pkg/apis/meta v1.1.1 - github.com/fluxcd/pkg/runtime v0.35.0 + github.com/fluxcd/pkg/apis/meta v1.1.2 + github.com/fluxcd/pkg/runtime v0.42.0 github.com/fluxcd/pkg/ssa v0.27.0 github.com/fluxcd/source-controller/api v1.0.0 github.com/go-git/go-git/v5 v5.6.1 @@ -37,8 +37,8 @@ require ( github.com/minio/minio-go/v7 v7.0.31 github.com/mitchellh/go-ps v1.0.0 github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 - github.com/onsi/ginkgo/v2 v2.9.5 - github.com/onsi/gomega v1.27.7 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.10 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 @@ -49,7 +49,7 @@ require ( github.com/weaveworks/policy-agent/api v1.0.5 github.com/weaveworks/tf-controller/tfctl v0.0.0-20230523160934-c6613bfc7fff github.com/yannh/kubeconform v0.5.0 - go.uber.org/zap v1.24.0 + go.uber.org/zap v1.25.0 golang.org/x/crypto v0.13.0 golang.org/x/oauth2 v0.7.0 google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 @@ -58,13 +58,13 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible - k8s.io/api v0.27.3 + k8s.io/api v0.27.4 k8s.io/apiextensions-apiserver v0.27.3 - k8s.io/apimachinery v0.27.3 + k8s.io/apimachinery v0.27.4 k8s.io/cli-runtime v0.26.8 - k8s.io/client-go v0.27.3 - sigs.k8s.io/cli-utils v0.34.0 - sigs.k8s.io/controller-runtime v0.15.0 + k8s.io/client-go v0.27.4 + sigs.k8s.io/cli-utils v0.35.0 + sigs.k8s.io/controller-runtime v0.15.1 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/yaml v1.3.0 ) @@ -73,7 +73,6 @@ require ( github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go v1.44.137 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cloudflare/circl v1.3.3 // indirect @@ -157,7 +156,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 - github.com/hashicorp/go-retryablehttp v0.7.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.13 // indirect @@ -185,10 +184,10 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect @@ -203,20 +202,20 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect go.uber.org/atomic v1.10.0 - go.uber.org/multierr v1.8.0 // indirect + go.uber.org/multierr v1.10.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.15.0 golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.12.0 golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.9.3 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/component-base v0.27.3 // indirect + k8s.io/component-base v0.27.4 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.26.8 diff --git a/go.sum b/go.sum index 1880459cd6..388a06927f 100644 --- a/go.sum +++ b/go.sum @@ -85,7 +85,6 @@ github.com/aws/aws-sdk-go v1.44.137 h1:GH2bUPiW7/gHtB04NxQOSOrKqFNjLGKmqt5YaO+K1 github.com/aws/aws-sdk-go v1.44.137/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -179,10 +178,10 @@ github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6 github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8= github.com/fluxcd/pkg/apis/kustomize v1.1.1 h1:MSGn4z0R9PptmoPFHnx2nEZ8Jtl1sKfw0cuDQY2HYwM= github.com/fluxcd/pkg/apis/kustomize v1.1.1/go.mod h1:0pCu0ecIY+ZM0iE/hOHYwCMZ3b0SpBrjJ1SH3FFyYdE= -github.com/fluxcd/pkg/apis/meta v1.1.1 h1:sLAKLbEu7rRzJ+Mytffu3NcpfdbOBTa6hcpOQzFWm+M= -github.com/fluxcd/pkg/apis/meta v1.1.1/go.mod h1:soCfzjFWbm1mqybDcOywWKTCEYlH3skpoNGTboVk234= -github.com/fluxcd/pkg/runtime v0.35.0 h1:9PYLcul8qdfLYQArcYpHe/QuMqyhAGGFN9F7uY/QVX4= -github.com/fluxcd/pkg/runtime v0.35.0/go.mod h1:sAaSTH8RHj3Y99xj0AtAndDTe5cv0DP4enyLV62EO78= +github.com/fluxcd/pkg/apis/meta v1.1.2 h1:Unjo7hxadtB2dvGpeFqZZUdsjpRA08YYSBb7dF2WIAM= +github.com/fluxcd/pkg/apis/meta v1.1.2/go.mod h1:BHQyRHCskGMEDf6kDGbgQ+cyiNpUHbLsCOsaMYM2maI= +github.com/fluxcd/pkg/runtime v0.42.0 h1:a5DQ/f90YjoHBmiXZUpnp4bDSLORjInbmqP7K11L4uY= +github.com/fluxcd/pkg/runtime v0.42.0/go.mod h1:p6A3xWVV8cKLLQW0N90GehKgGMMmbNYv+OSJ/0qB0vg= github.com/fluxcd/pkg/ssa v0.27.0 h1:BJnWDy3xDtYD2U+sVZPkoh6PfnQKoXsklO0pzojU8XU= github.com/fluxcd/pkg/ssa v0.27.0/go.mod h1:fxvmVf4FxodJi5lTglMcL8JsF6hfJLG99C56/CgchH0= github.com/fluxcd/source-controller/api v1.0.0 h1:lPjmCXmEiI3tY4pReeVQBMuyLgdH8462W5ewUa9kgYM= @@ -331,8 +330,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -478,12 +477,12 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= @@ -500,21 +499,21 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -569,7 +568,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/theckman/yacspin v0.13.12 h1:CdZ57+n0U6JMuh2xqjnjRq5Haj6v1ner2djtLQRzJr4= @@ -619,10 +618,11 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -905,8 +905,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1047,7 +1047,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1060,18 +1059,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= -k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= +k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= +k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= +k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/cli-runtime v0.26.8 h1:LFiS+z20j8gt9Iyo4EsbivzrDYPRbFFj8wmpwdhy7cQ= k8s.io/cli-runtime v0.26.8/go.mod h1:j3YQ0OtQnqsQRsMWbmZrKqbOvN2OUu0K+dPffeKPVj0= -k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= -k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= -k8s.io/component-base v0.27.3 h1:g078YmdcdTfrCE4fFobt7qmVXwS8J/3cI1XxRi/2+6k= -k8s.io/component-base v0.27.3/go.mod h1:JNiKYcGImpQ44iwSYs6dysxzR9SxIIgQalk4HaCNVUY= +k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= +k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= +k8s.io/component-base v0.27.4 h1:Wqc0jMKEDGjKXdae8hBXeskRP//vu1m6ypC+gwErj4c= +k8s.io/component-base v0.27.4/go.mod h1:hoiEETnLc0ioLv6WPeDt8vD34DDeB35MfQnxCARq3kY= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= @@ -1084,10 +1083,10 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= -sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= -sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= -sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= +sigs.k8s.io/cli-utils v0.35.0 h1:dfSJaF1W0frW74PtjwiyoB4cwdRygbHnC7qe7HF0g/Y= +sigs.k8s.io/cli-utils v0.35.0/go.mod h1:ITitykCJxP1vaj1Cew/FZEaVJ2YsTN9Q71m02jebkoE= +sigs.k8s.io/controller-runtime v0.15.1 h1:9UvgKD4ZJGcj24vefUFgZFP3xej/3igL9BsOUTb/+4c= +sigs.k8s.io/controller-runtime v0.15.1/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= 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.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 182e873f5f706562bb7d63e5e0a9cd56f3f258d9 Mon Sep 17 00:00:00 2001 From: "a.shabaan" Date: Tue, 31 Oct 2023 14:02:26 +0200 Subject: [PATCH 07/23] Cleanup datatable (#4091) * cleanup datatable * sort filtered items by `sortValue || value` --- ui/components/DataTable/DataTable.tsx | 249 ++++-------------- ui/components/DataTable/SortableLabel.tsx | 6 +- .../TableView/SearchedNamespacesModal.tsx | 33 +++ .../DataTable/TableView/SortableLabelView.tsx | 52 ++++ .../DataTable/TableView/TableBody.tsx | 87 ++++++ .../DataTable/TableView/TableHeader.tsx | 51 ++++ .../DataTable/TableView/TableView.tsx | 57 ++++ ui/components/DataTable/TableView/types.ts | 44 ++++ .../__tests__/DataTableFilters.test.tsx | 3 +- .../__snapshots__/DataTable.test.tsx.snap | 115 ++++---- .../__tests__/FluxObjectsTable.test.tsx | 12 +- 11 files changed, 432 insertions(+), 277 deletions(-) create mode 100644 ui/components/DataTable/TableView/SearchedNamespacesModal.tsx create mode 100644 ui/components/DataTable/TableView/SortableLabelView.tsx create mode 100644 ui/components/DataTable/TableView/TableBody.tsx create mode 100644 ui/components/DataTable/TableView/TableHeader.tsx create mode 100644 ui/components/DataTable/TableView/TableView.tsx create mode 100644 ui/components/DataTable/TableView/types.ts diff --git a/ui/components/DataTable/DataTable.tsx b/ui/components/DataTable/DataTable.tsx index 408f9c1b24..1a676cec58 100644 --- a/ui/components/DataTable/DataTable.tsx +++ b/ui/components/DataTable/DataTable.tsx @@ -1,12 +1,3 @@ -import { - Checkbox, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, -} from "@material-ui/core"; import _ from "lodash"; import qs from "query-string"; import * as React from "react"; @@ -24,31 +15,25 @@ import FilterDialog, { } from "../FilterDialog"; import Flex from "../Flex"; import Icon, { IconType } from "../Icon"; -import InfoModal from "../InfoModal"; import SearchField from "../SearchField"; -import Spacer from "../Spacer"; -import Text from "../Text"; import { filterRows, filterSelectionsToQueryString, filterText, initialFormState, parseFilterStateFromURL, - sortByField, toPairs, } from "./helpers"; -import SortableLabel from "./SortableLabel"; + +import SearchedNamespacesModal from "./TableView/SearchedNamespacesModal"; +import TableView from "./TableView/TableView"; +import { SortField } from "./TableView/types"; import { Field, FilterState } from "./types"; -/** DataTable Properties */ export interface Props { - /** The ID of the table. */ id?: string; - /** CSS MUI Overrides or other styling. */ className?: string; - /** A list of objects with four fields: `label`, which is a string representing the column header, `value`, which can be a string, or a function that extracts the data needed to fill the table cell, and `sortValue`, which customizes your input to the search function */ fields: Field[]; - /** A list of data that will be iterated through to create the columns described in `fields`. */ rows?: any[]; filters?: FilterConfig; dialogOpen?: boolean; @@ -59,12 +44,6 @@ export interface Props { disableSort?: boolean; searchedNamespaces?: SearchedNamespaces; } -//styled components -const EmptyRow = styled(TableRow)<{ colSpan: number }>` - td { - text-align: center; - } -`; const TopBar = styled(Flex)` max-width: 100%; @@ -75,7 +54,6 @@ const IconFlex = styled(Flex)` padding: 0 ${(props) => props.theme.spacing.small}; `; -/** Form DataTable */ function UnstyledDataTable({ id, className, @@ -90,21 +68,31 @@ function UnstyledDataTable({ disableSort, searchedNamespaces, }: Props) { - //URL info const history = useHistory(); const location = useLocation(); const search = location.search; const state = parseFilterStateFromURL(search); - const [filterDialogOpen, setFilterDialogOpen] = React.useState(dialogOpen); - const [searchedNamespacesModalOpen, setSearchedNamespacesModalOpen] = - React.useState(false); + + const [checked, setChecked] = React.useState([]); + const [filterState, setFilterState] = React.useState({ filters: selectionsToFilters(state.initialSelections, filters), formState: initialFormState(filters, state.initialSelections), textFilters: state.textFilters, }); + const [sortedItem, setSortedItem] = React.useState(() => { + const defaultSortField = fields.find((f) => f.defaultSort); + const sortField = defaultSortField + ? { + ...defaultSortField, + reverseSort: false, + } + : null; + return sortField; + }); + const handleFilterChange = (sel: FilterSelections) => { const filterQuery = filterSelectionsToQueryString(sel); history.replace({ ...location, search: filterQuery }); @@ -114,6 +102,20 @@ function UnstyledDataTable({ filtered = filterText(filtered, fields, filterState.textFilters); const chips = toPairs(filterState); + const sortItems = (filtered) => { + let sorted = filtered; + if (sortedItem) { + sorted = _.orderBy( + filtered, + [sortedItem.sortValue || sortedItem.value], + [sortedItem.reverseSort ? "desc" : "asc"] + ); + } + return sorted; + }; + + const items = sortItems(filtered); + const doChange = (formState) => { if (handleFilterChange) { handleFilterChange(formState); @@ -176,95 +178,8 @@ function UnstyledDataTable({ setFilterState({ ...filterState, filters, formState }); }; - const [sortFieldIndex, setSortFieldIndex] = React.useState(() => { - let sortFieldIndex = fields.findIndex((f) => f.defaultSort); - - if (sortFieldIndex === -1) { - sortFieldIndex = 0; - } - - return sortFieldIndex; - }); - - const secondarySortFieldIndex = fields.findIndex((f) => f.secondarySort); - - const [reverseSort, setReverseSort] = React.useState(false); - - let sortFields = [fields[sortFieldIndex]]; - - const useSecondarySort = - secondarySortFieldIndex > -1 && sortFieldIndex != secondarySortFieldIndex; - - if (useSecondarySort) { - sortFields = sortFields.concat(fields[secondarySortFieldIndex]); - sortFields = sortFields.concat( - fields.filter( - (_, index) => - index != sortFieldIndex && index != secondarySortFieldIndex - ) - ); - } else { - sortFields = sortFields.concat( - fields.filter((_, index) => index != sortFieldIndex) - ); - } - - const sorted = sortByField( - filtered, - reverseSort, - sortFields, - useSecondarySort, - disableSort - ); - - const numFields = fields.length + (checkboxes ? 1 : 0); - - const [checked, setChecked] = React.useState([]); - - const r = _.map(sorted, (r, i) => { - return ( - - {checkboxes && ( - - { - if (e.target.checked) setChecked([...checked, r.uid]); - else setChecked(_.without(checked, r.uid)); - }} - color="primary" - /> - - )} - {_.map(fields, (f) => { - const style: React.CSSProperties = { - ...(f.minWidth && { minWidth: f.minWidth }), - ...(f.maxWidth && { maxWidth: f.maxWidth }), - }; - - return ( - 0 ? style : undefined} - key={f.label} - > - - {(typeof f.value === "function" ? f.value(r) : r[f.value]) || - "-"} - - - ); - })} - - ); - }); - return ( - {checkboxes && } {filters && !hideSearchAndFilters && ( @@ -276,18 +191,9 @@ function UnstyledDataTable({ /> {searchedNamespaces && ( - - setSearchedNamespacesModalOpen(!searchedNamespacesModalOpen) - } - variant="text" - > - - + )} - - - - - {checkboxes && ( - - - e.target.checked - ? setChecked(filtered?.map((r) => r.uid)) - : setChecked([]) - } - color="primary" - /> - - )} - {_.map(fields, (f, index) => ( - - {typeof f.labelRenderer === "function" ? ( - f.labelRenderer(r) - ) : ( - { - if (onColumnHeaderClick) { - onColumnHeaderClick(f); - } - - setSortFieldIndex(...args); - }} - setReverseSort={(isReverse) => { - if (onColumnHeaderClick) { - onColumnHeaderClick(f); - } - - setReverseSort(isReverse); - }} - /> - )} - - ))} - - - - {r.length > 0 ? ( - r - ) : ( - - - - - - {emptyMessagePlaceholder || ( - No data - )} - - - - )} - -
-
+ setChecked(checked)} + onSortChange={(field) => { + if (onColumnHeaderClick) onColumnHeaderClick(field); + setSortedItem(field); + }} + /> {!hideSearchAndFilters && ( void; }; -const TableButton = styled(Button)` +export const TableButton = styled(Button)` &.MuiButton-root { margin: 0; text-transform: none; @@ -44,7 +43,7 @@ export default function SortableLabel({ const sort = fields[sortFieldIndex]; return ( - + - {sort.label === field.label ? ( { + const [searchedNamespacesModalOpen, setSearchedNamespacesModalOpen] = + React.useState(false); + return ( + <> + + setSearchedNamespacesModalOpen(!searchedNamespacesModalOpen) + } + variant="text" + > + + + + + ); +}; + +export default SearchedNamespacesModal; diff --git a/ui/components/DataTable/TableView/SortableLabelView.tsx b/ui/components/DataTable/TableView/SortableLabelView.tsx new file mode 100644 index 0000000000..afaf22ba4a --- /dev/null +++ b/ui/components/DataTable/TableView/SortableLabelView.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import Flex from "../../Flex"; +import Icon, { IconType } from "../../Icon"; +import { TableButton } from "../SortableLabel"; +import { SortableLabelViewProps } from "./types"; + +const SortableLabelView = ({ + field, + onSortClick, + setSortedField, + sortedField, +}: SortableLabelViewProps) => { + return ( + + { + setSortedField({ + ...field, + reverseSort: + sortedField?.label === field.label + ? !sortedField.reverseSort + : false, + }); + onSortClick({ + ...field, + reverseSort: + sortedField?.label === field.label + ? !sortedField.reverseSort + : false, + }); + }} + > +

+ {field.label} +

+
+ {sortedField?.label === field.label ? ( + + ) : ( +
+ )} + + ); +}; + +export default SortableLabelView; diff --git a/ui/components/DataTable/TableView/TableBody.tsx b/ui/components/DataTable/TableView/TableBody.tsx new file mode 100644 index 0000000000..f923ca3cb5 --- /dev/null +++ b/ui/components/DataTable/TableView/TableBody.tsx @@ -0,0 +1,87 @@ +import { Checkbox, TableBody, TableCell, TableRow } from "@material-ui/core"; +import React from "react"; +import styled from "styled-components"; +import Flex from "../../Flex"; +import Icon, { IconType } from "../../Icon"; +import Text from "../../Text"; +import { TableBodyViewProps } from "./types"; + +const EmptyRow = styled(TableRow)<{ colSpan: number }>` + td { + text-align: center; + } +`; + +const TableBodyView = ({ + rows, + fields, + hasCheckboxes, + checkedFields, + emptyMessagePlaceholder, + onCheckChange, +}: TableBodyViewProps) => { + if (rows.length === 0) { + const numFields = fields.length + (hasCheckboxes ? 1 : 0); + return ( + + + + + + {emptyMessagePlaceholder || ( + No data + )} + + + + + ); + } + + return ( + + {rows?.map((r, i) => { + return ( + + {hasCheckboxes && ( + + c === r.uid) > -1} + onChange={(e) => { + onCheckChange(e.target.checked, r.uid); + }} + color="primary" + /> + + )} + {fields?.map((f) => { + const style: React.CSSProperties = { + ...(f.minWidth && { minWidth: f.minWidth }), + ...(f.maxWidth && { maxWidth: f.maxWidth }), + }; + + return ( + 0 ? style : undefined} + key={f.label} + > + + {(typeof f.value === "function" + ? f.value(r) + : r[f.value]) || "-"} + + + ); + })} + + ); + })} + + ); +}; + +export default TableBodyView; diff --git a/ui/components/DataTable/TableView/TableHeader.tsx b/ui/components/DataTable/TableView/TableHeader.tsx new file mode 100644 index 0000000000..e8763d9666 --- /dev/null +++ b/ui/components/DataTable/TableView/TableHeader.tsx @@ -0,0 +1,51 @@ +import { Checkbox, TableCell, TableHead, TableRow } from "@material-ui/core"; +import React from "react"; +import SortableLabelView from "./SortableLabelView"; +import { SortField, TableHeaderProps } from "./types"; + +const TableHeader = ({ + fields, + hasCheckboxes, + checked, + defaultSortedField, + onSortChange, + onCheckChange, +}: TableHeaderProps) => { + const [sortedField, setSortedField] = + React.useState(defaultSortedField); + return ( + + + {hasCheckboxes && ( + + { + onCheckChange(e.target.checked); + }} + color="primary" + /> + + )} + {fields.map((f) => ( + + {typeof f.labelRenderer === "function" ? ( + f.labelRenderer(f) + ) : ( + { + onSortChange(f); + }} + /> + )} + + ))} + + + ); +}; + +export default TableHeader; diff --git a/ui/components/DataTable/TableView/TableView.tsx b/ui/components/DataTable/TableView/TableView.tsx new file mode 100644 index 0000000000..2b02a586cc --- /dev/null +++ b/ui/components/DataTable/TableView/TableView.tsx @@ -0,0 +1,57 @@ +import { Table, TableContainer } from "@material-ui/core"; +import React from "react"; +import TableBodyView from "./TableBody"; +import TableHeader from "./TableHeader"; +import { TableViewProps } from "./types"; + +const TableView = ({ + rows, + fields, + id, + checkedFields, + defaultSortedField, + onSortChange, + hasCheckboxes, + onBatchCheck, +}: TableViewProps) => { + const onCheckChange = (checked: boolean, id: string) => { + const selectedFields = [...checkedFields]; + if (checked) { + selectedFields.push(id); + } else { + selectedFields.splice(selectedFields.indexOf(id), 1); + } + onBatchCheck(selectedFields); + }; + const onHeaderCheckChange = (checked: boolean) => { + if (checked) { + checkedFields = rows.map((r) => r.uid); + } else { + checkedFields = []; + } + onBatchCheck(checkedFields); + }; + return ( + + + + +
+
+ ); +}; + +export default TableView; diff --git a/ui/components/DataTable/TableView/types.ts b/ui/components/DataTable/TableView/types.ts new file mode 100644 index 0000000000..6ee7c6e9f4 --- /dev/null +++ b/ui/components/DataTable/TableView/types.ts @@ -0,0 +1,44 @@ +import { Field } from "../types"; + +export interface SortField extends Field { + reverseSort: boolean; +} + +export interface SortableLabelViewProps { + field: Field; + onSortClick?: (field: SortField) => void; + sortedField?: SortField; + setSortedField: React.Dispatch>; +} + +export interface TableBodyViewProps { + rows: any[]; + fields: Field[]; + hasCheckboxes?: boolean; + checkedFields?: string[]; + emptyMessagePlaceholder?: React.ReactNode; + onCheckChange?: (checked: boolean, id: string) => void; +} + +export interface TableHeaderProps { + fields: Field[]; + defaultSortedField?: SortField; + hasCheckboxes?: boolean; + checked?: boolean; + onSortChange?: (field: SortField) => void; + onCheckChange?: (checked: boolean) => void; +} + +export interface TableViewProps { + id?: string; + className?: string; + fields: Field[]; + defaultSortedField?: SortField; + rows?: any[]; + onSortChange?: (field: SortField) => void; + onBatchCheck?: (uids: string[]) => void; + hasCheckboxes?: boolean; + checkedFields?: string[]; + emptyMessagePlaceholder?: React.ReactNode; + disableSort?: boolean; +} diff --git a/ui/components/DataTable/__tests__/DataTableFilters.test.tsx b/ui/components/DataTable/__tests__/DataTableFilters.test.tsx index 799c73be21..6f44692723 100644 --- a/ui/components/DataTable/__tests__/DataTableFilters.test.tsx +++ b/ui/components/DataTable/__tests__/DataTableFilters.test.tsx @@ -237,7 +237,8 @@ describe("DataTableFilters", () => { const tableRows2 = document.querySelectorAll("tbody tr"); expect(tableRows2).toHaveLength(3); - expect(tableRows2[1].innerHTML).toContain("rad"); + + expect(tableRows2[2].innerHTML).toContain("rad"); const chip2 = screen.getByText(`type${filterSeparator}baz`); expect(chip2).toBeTruthy(); diff --git a/ui/components/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap b/ui/components/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap index c0682a899e..5114a5e7c1 100644 --- a/ui/components/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/ui/components/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -64,13 +64,14 @@ exports[`DataTable snapshots renders 1`] = ` -webkit-box-align: center; -ms-flex-align: center; align-items: center; + gap: 4px; -webkit-box-pack: start; -webkit-justify-content: flex-start; -ms-flex-pack: start; justify-content: flex-start; } -.c8 { +.c7 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -84,7 +85,7 @@ exports[`DataTable snapshots renders 1`] = ` align-items: center; } -.c14 { +.c13 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -102,7 +103,7 @@ exports[`DataTable snapshots renders 1`] = ` justify-content: flex-start; } -.c16 { +.c15 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -121,7 +122,7 @@ exports[`DataTable snapshots renders 1`] = ` justify-content: flex-start; } -.c11 { +.c10 { font-family: 'proxima-nova',Helvetica,Arial,sans-serif; font-size: 14px; font-weight: 400; @@ -129,7 +130,7 @@ exports[`DataTable snapshots renders 1`] = ` font-style: normal; } -.c17 { +.c16 { font-family: 'proxima-nova',Helvetica,Arial,sans-serif; font-size: 20px; font-weight: 400; @@ -138,63 +139,59 @@ exports[`DataTable snapshots renders 1`] = ` color: #737373; } -.c9 svg { +.c8 svg { height: 16px; width: 16px; } -.c9 svg path.path-fill, -.c9 svg line.path-fill, -.c9 svg polygon.path-fill, -.c9 svg rect.path-fill, -.c9 svg circle.path-fill, -.c9 svg polyline.path-fill { +.c8 svg path.path-fill, +.c8 svg line.path-fill, +.c8 svg polygon.path-fill, +.c8 svg rect.path-fill, +.c8 svg circle.path-fill, +.c8 svg polyline.path-fill { fill: !important; -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; } -.c9 svg path.stroke-fill, -.c9 svg line.stroke-fill, -.c9 svg polygon.stroke-fill, -.c9 svg rect.stroke-fill, -.c9 svg circle.stroke-fill, -.c9 svg polyline.stroke-fill { +.c8 svg path.stroke-fill, +.c8 svg line.stroke-fill, +.c8 svg polygon.stroke-fill, +.c8 svg rect.stroke-fill, +.c8 svg circle.stroke-fill, +.c8 svg polyline.stroke-fill { stroke: !important; -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; } -.c9 svg rect.rect-height { +.c8 svg rect.rect-height { height: 16px; width: 16px; } -.c9.downward { +.c8.downward { -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } -.c9.upward { +.c8.upward { -webkit-transform: initial; -ms-transform: initial; transform: initial; } -.c9 .c10 { +.c8 .c9 { margin-left: 4px; } -.c9 img { +.c8 img { width: 16px; } -.c7 { - padding: 4px; -} - -.c12 { +.c11 { position: relative; height: 100%; width: 0px; @@ -207,12 +204,12 @@ exports[`DataTable snapshots renders 1`] = ` transition-timing-function: ease-in-out; } -.c12.open { +.c11.open { left: 0px; width: 350px; } -.c13 { +.c12 { background: rbga(255,255,255,0.75); height: 100%; width: 100%; @@ -221,15 +218,15 @@ exports[`DataTable snapshots renders 1`] = ` padding-left: 32px; } -.c15 .MuiListItem-gutters { +.c14 .MuiListItem-gutters { padding-left: 0px; } -.c15 .MuiCheckbox-root { +.c14 .MuiCheckbox-root { padding: 0px; } -.c15 .MuiCheckbox-colorSecondary.Mui-checked { +.c14 .MuiCheckbox-colorSecondary.Mui-checked { color: #00b3ec; } @@ -326,9 +323,6 @@ exports[`DataTable snapshots renders 1`] = `
-
@@ -339,7 +333,6 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableContainer-root" > @@ -388,10 +381,7 @@ exports[`DataTable snapshots renders 1`] = `
-
-
-
-
Ready @@ -591,7 +572,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 2004-01-02T15:04:05-0700 @@ -602,7 +583,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 3000 @@ -618,7 +599,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > - @@ -644,7 +625,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 2006-01-02T15:04:05-0700 @@ -655,7 +636,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 2000 @@ -671,7 +652,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > @@ -695,7 +676,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 2005-01-02T15:04:05-0700 @@ -706,7 +687,7 @@ exports[`DataTable snapshots renders 1`] = ` className="MuiTableCell-root MuiTableCell-body" > 1000 @@ -717,20 +698,20 @@ exports[`DataTable snapshots renders 1`] = `
diff --git a/ui/components/__tests__/FluxObjectsTable.test.tsx b/ui/components/__tests__/FluxObjectsTable.test.tsx index fe81af6c81..8d22b9f0a7 100644 --- a/ui/components/__tests__/FluxObjectsTable.test.tsx +++ b/ui/components/__tests__/FluxObjectsTable.test.tsx @@ -61,17 +61,17 @@ describe("FluxObjectsTable", () => { ); const rows = document.querySelectorAll("tbody tr"); - const deploymentName = rows[0].querySelector("td:first-child"); - const link = deploymentName.querySelector("a"); - - expect(link.href).toEqual("http://localhost/some-cool-url"); - // Since our resolver does not specify any behavior for a Service, // this should not have a link. - const serviceName = rows[1].querySelector("td:first-child"); + const serviceName = rows[0].querySelector("td:first-child"); const serviceLink = serviceName.querySelector("a"); expect(serviceLink).toBeFalsy(); + + const deploymentName = rows[1].querySelector("td:first-child"); + const link = deploymentName.querySelector("a"); + + expect(link.href).toEqual("http://localhost/some-cool-url"); }); it("runs the onClick handler", () => { const onClick = jest.fn(); From 956bd52cdef7fe9ef80e713c80ffa4fa6c9d02af Mon Sep 17 00:00:00 2001 From: Balazs Nadasdi Date: Tue, 31 Oct 2023 15:11:31 +0100 Subject: [PATCH 08/23] Update pkg/run/session/connect/connect.go Co-authored-by: Yiannis Triantafyllopoulos <8741709+yiannistri@users.noreply.github.com> --- pkg/run/session/connect/connect.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/run/session/connect/connect.go b/pkg/run/session/connect/connect.go index f2ac4bd046..3d14420a4d 100644 --- a/pkg/run/session/connect/connect.go +++ b/pkg/run/session/connect/connect.go @@ -607,7 +607,7 @@ func (conn *Connection) createServiceAccountToken(vKubeConfig api.Config) (strin token := "" conn.Log.Actionf("Create service account token for %s/%s", serviceAccountNamespace, serviceAccount) - err = wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { + err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute*3, true, func(_ context.Context) (bool, error) { // check if namespace exists _, err := vKubeClient.CoreV1().Namespaces().Get(context.TODO(), serviceAccountNamespace, metav1.GetOptions{}) if err != nil { From d39a3d6c4170c2f3cd1674e56ffed8fd513a6f80 Mon Sep 17 00:00:00 2001 From: Luiz Filho Date: Tue, 31 Oct 2023 11:46:56 -0300 Subject: [PATCH 09/23] Refactoring Status column (#4098) --- ui/components/FluxObjectsTable.tsx | 45 ++---------------------------- 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/ui/components/FluxObjectsTable.tsx b/ui/components/FluxObjectsTable.tsx index b39461223e..8911c4fcc7 100644 --- a/ui/components/FluxObjectsTable.tsx +++ b/ui/components/FluxObjectsTable.tsx @@ -5,19 +5,14 @@ import { useLinkResolver } from "../contexts/LinkResolverContext"; import { Kind } from "../lib/api/core/types.pb"; import { formatURL, objectTypeToRoute } from "../lib/nav"; import { FluxObject } from "../lib/objects"; -import { makeImageString, statusSortHelper } from "../lib/utils"; +import { makeImageString } from "../lib/utils"; import DataTable from "./DataTable"; import { DetailViewProps } from "./DetailModal"; import HealthCheckStatusIndicator, { HealthStatusType, } from "./HealthCheckStatusIndicator"; import ImageLink from "./ImageLink"; -import KubeStatusIndicator, { - ReadyStatusValue, - SpecialObject, - computeMessage, - createSyntheticCondition, -} from "./KubeStatusIndicator"; +import { computeMessage } from "./KubeStatusIndicator"; import Link from "./Link"; import Text from "./Text"; @@ -93,7 +88,7 @@ function FluxObjectsTable({ sortValue: ({ namespace }) => namespace, }, { - label: "Health Check", + label: "Status", value: ({ health }) => { return health.status !== HealthStatusType.Unknown ? ( @@ -101,40 +96,6 @@ function FluxObjectsTable({ }, sortValue: ({ health }: FluxObject) => health.status, }, - { - label: "Status", - value: (u: FluxObject) => { - const status = u.obj.status; - - if (!status || !status.conditions) { - const cond = createSyntheticCondition( - u.type as SpecialObject, - status - ); - - if (cond.status === ReadyStatusValue.Unknown) { - return null; - } - - return ( - - ); - } - - return u.conditions.length > 0 ? ( - - ) : null; - }, - sortValue: statusSortHelper, - }, { label: "Message", value: (u: FluxObject) => _.first(u.conditions)?.message, From 860f636c9c32f45d7138473a54f277584489efd8 Mon Sep 17 00:00:00 2001 From: yiannis Date: Tue, 31 Oct 2023 14:15:05 +0000 Subject: [PATCH 10/23] fix: Remove GitOps Run CLI commands --- cmd/gitops/beta/cmd.go | 18 - cmd/gitops/beta/run/cleanup.go | 62 - cmd/gitops/beta/run/cleanup_test.go | 87 -- cmd/gitops/beta/run/cmd.go | 1338 ------------------ cmd/gitops/beta/run/trim_k8s_version.go | 14 - cmd/gitops/beta/run/trim_k9s_version_test.go | 79 -- cmd/gitops/remove/cmd.go | 18 - cmd/gitops/remove/run/cmd.go | 202 --- cmd/gitops/root/cmd.go | 16 +- 9 files changed, 4 insertions(+), 1830 deletions(-) delete mode 100644 cmd/gitops/beta/cmd.go delete mode 100644 cmd/gitops/beta/run/cleanup.go delete mode 100644 cmd/gitops/beta/run/cleanup_test.go delete mode 100644 cmd/gitops/beta/run/cmd.go delete mode 100644 cmd/gitops/beta/run/trim_k8s_version.go delete mode 100644 cmd/gitops/beta/run/trim_k9s_version_test.go delete mode 100644 cmd/gitops/remove/cmd.go delete mode 100644 cmd/gitops/remove/run/cmd.go diff --git a/cmd/gitops/beta/cmd.go b/cmd/gitops/beta/cmd.go deleted file mode 100644 index 9edb04cea2..0000000000 --- a/cmd/gitops/beta/cmd.go +++ /dev/null @@ -1,18 +0,0 @@ -package beta - -import ( - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" -) - -func GetCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "beta", - Short: "This component contains unstable or still-in-development functionality", - } - - cmd.AddCommand(run.RunCommand(opts)) - - return cmd -} diff --git a/cmd/gitops/beta/run/cleanup.go b/cmd/gitops/beta/run/cleanup.go deleted file mode 100644 index 3c6ed21d0d..0000000000 --- a/cmd/gitops/beta/run/cleanup.go +++ /dev/null @@ -1,62 +0,0 @@ -package run - -import ( - "context" - "errors" - "fmt" - - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -// CleanupFunc is a function supposed to be called while a GitOps Run session -// is terminating. Each component creating resources on the cluster should -// return such a function that is then added to the CleanupFuncs stack by -// the orchestrating code and removed from it and executed during shutdown. -type CleanupFunc func(ctx context.Context, log logger.Logger) error - -// CleanupFuncs is a stack holding CleanupFunc references that are used -// to roll up all resources created during an GitOps Run session as soon -// as the session is terminated. -type CleanupFuncs struct { - fns []CleanupFunc -} - -// Push implements the stack's Push operation, adding the given CleanupFunc -// to the top of the stack. -func (c *CleanupFuncs) Push(f CleanupFunc) { - if f == nil { - return - } - - c.fns = append(c.fns, f) -} - -var ErrEmptyStack = errors.New("stack is empty") - -// Pop implements the stack's Pop operation, returning and removing -// the top CleanupFunc from the stack. -func (c *CleanupFuncs) Pop() (CleanupFunc, error) { - if len(c.fns) == 0 { - return nil, ErrEmptyStack - } - var res CleanupFunc - res, c.fns = c.fns[len(c.fns)-1], c.fns[:len(c.fns)-1] - return res, nil -} - -func CleanupCluster(ctx context.Context, log logger.Logger, fns CleanupFuncs) error { - for { - fn, err := fns.Pop() - if err != nil { - if err != ErrEmptyStack { - return fmt.Errorf("failed fetching next cleanup function: %w", err) - } - break - } - if err := fn(ctx, log); err != nil { - log.Failuref("failed cleaning up: %s", err) - } - } - - return nil -} diff --git a/cmd/gitops/beta/run/cleanup_test.go b/cmd/gitops/beta/run/cleanup_test.go deleted file mode 100644 index 4e1c2ed9ee..0000000000 --- a/cmd/gitops/beta/run/cleanup_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package run_test - -import ( - "context" - "fmt" - "reflect" - "strings" - "testing" - - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -func TestCleanupWorksWithNil(t *testing.T) { - g := NewWithT(t) - - var s run.CleanupFuncs - f1 := func(ctx context.Context, log logger.Logger) error { return nil } - f2 := func(ctx context.Context, log logger.Logger) error { return nil } - - s.Push(f1) - s.Push(nil) // should be ignored - s.Push(f2) - - fn, err := s.Pop() - g.Expect(err).NotTo(HaveOccurred(), "pop returned an unexpected error") - g.Expect( - reflect.ValueOf(fn).Pointer()). - To(Equal(reflect.ValueOf(f2).Pointer()), "value returned from stack is not f2") - - fn, err = s.Pop() - g.Expect(err).NotTo(HaveOccurred(), "pop returned an unexpected error") - g.Expect( - reflect.ValueOf(fn).Pointer()). - To(Equal(reflect.ValueOf(f1).Pointer()), "value returned from stack is not f1") - - fn, err = s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "pop returned an unexpected error") - g.Expect(fn).To(BeNil(), "unexpected value returned from stack") -} - -func TestCleanupFailsGracefullyOnConsecutiveCallsToPop(t *testing.T) { - g := NewWithT(t) - - var s run.CleanupFuncs - fn, err := s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "unexpected error returned from pop") - g.Expect(fn).To(BeNil(), "unexpected value returned from pop") - - fn, err = s.Pop() - g.Expect(err).To(Equal(run.ErrEmptyStack), "unexpected error returned from pop") - g.Expect(fn).To(BeNil(), "unexpected value returned from pop") -} - -func TestCleanupClusterRunsAllFunctionsFromStackInCorrectOrder(t *testing.T) { - g := NewWithT(t) - - cnt := 0 - var s run.CleanupFuncs - - s.Push(func(ctx context.Context, log logger.Logger) error { cnt *= 2; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt += 4; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt = 3; return nil }) - - err := run.CleanupCluster(context.Background(), nil, s) - g.Expect(err).NotTo(HaveOccurred(), "unexpected error returned") - g.Expect(cnt).To(Equal(14), "unexpected execution order") -} - -func TestCleanupClusterLogsAllErrors(t *testing.T) { - g := NewWithT(t) - - cnt := 0 - var s run.CleanupFuncs - - s.Push(func(ctx context.Context, log logger.Logger) error { cnt *= 2; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt += 4; return nil }) - s.Push(func(ctx context.Context, log logger.Logger) error { return fmt.Errorf("foo") }) - s.Push(func(ctx context.Context, log logger.Logger) error { cnt = 3; return nil }) - - var buf strings.Builder - err := run.CleanupCluster(context.Background(), logger.NewCLILogger(&buf), s) - g.Expect(err).NotTo(HaveOccurred(), "function should not have returned an error") - g.Expect(cnt).To(Equal(14), "unexpected execution order") - g.Expect(buf.String()).To(Equal("✗ failed cleaning up: foo\n"), "unexpected log output") -} diff --git a/cmd/gitops/beta/run/cmd.go b/cmd/gitops/beta/run/cmd.go deleted file mode 100644 index bcd6e8ee71..0000000000 --- a/cmd/gitops/beta/run/cmd.go +++ /dev/null @@ -1,1338 +0,0 @@ -package run - -import ( - "context" - "errors" - "fmt" - "io" - "io/fs" - "os" - "os/signal" - "os/user" - "path/filepath" - "strings" - "sync" - "sync/atomic" - "syscall" - "time" - - "github.com/fluxcd/go-git-providers/gitprovider" - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - sourcev1 "github.com/fluxcd/source-controller/api/v1" - "github.com/fsnotify/fsnotify" - "github.com/manifoldco/promptui" - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/cmderrors" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/pkg/fluxexec" - "github.com/weaveworks/weave-gitops/pkg/fluxinstall" - "github.com/weaveworks/weave-gitops/pkg/kube" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/bootstrap" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - "github.com/weaveworks/weave-gitops/pkg/run/install" - "github.com/weaveworks/weave-gitops/pkg/run/watch" - "github.com/weaveworks/weave-gitops/pkg/s3" - "github.com/weaveworks/weave-gitops/pkg/sourceignore" - "github.com/weaveworks/weave-gitops/pkg/validate" - "github.com/weaveworks/weave-gitops/pkg/version" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - "sigs.k8s.io/yaml" -) - -const ( - defaultDashboardName = "ww-gitops" - dashboardPodName = "ww-gitops-weave-gitops" - adminUsername = "admin" -) - -var HelmChartVersion = "3.0.0" - -type RunCommandFlags struct { - FluxVersion string - AllowK8sContext []string - Components []string - ComponentsExtra []string - Timeout time.Duration - PortForward string // port forward specifier, e.g. "port=8080:8080,resource=svc/app" - RootDir string - DecryptionKeyFile string - - // Dashboard - DashboardPort string - DashboardHashedPassword string - SkipDashboardInstall bool - DashboardImage string - DashboardValuesFiles []string - - // Session - SessionName string - SessionNamespace string - NoSession bool - SkipResourceCleanup bool - NoBootstrap bool - - // Global flags. - Namespace string - KubeConfig string - - // Flags, created by genericclioptions. - Context string - - // Hidden session name for the sub-process - HiddenSessionName string -} - -var flags RunCommandFlags - -var kubeConfigArgs *genericclioptions.ConfigFlags - -func RunCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "run", - Short: "Set up an interactive sync between your cluster and your local file system", - Long: "This will set up a sync between the cluster in your kubeconfig and the path that you specify on your local filesystem. If you do not have Flux installed on the cluster then this will add it to the cluster automatically. This is a requirement so we can sync the files successfully from your local system onto the cluster. Flux will take care of producing the objects for you.", - Example: ` -# Run the sync on the current working directory -gitops beta run . [flags] - -# Run the sync against the dev overlay path -gitops beta run ./deploy/overlays/dev - -# Run the sync on the dev directory and forward the port. -# Listen on port 8080 on localhost, forwarding to 5000 in a pod of the service app. -gitops beta run ./dev --port-forward port=8080:5000,resource=svc/app - -# Run the sync on the dev directory with a specified root dir. -gitops beta run ./clusters/default/dev --root-dir ./clusters/default - -# Run the sync on the podinfo demo. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./deploy/overlays/dev --no-session --timeout 3m --port-forward namespace=dev,resource=svc/backend,port=9898:9898 - -# Run the sync on the podinfo demo in the session mode. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./deploy/overlays/dev --timeout 3m --port-forward namespace=dev,resource=svc/backend,port=9898:9898 - -# Run the sync on the podinfo Helm chart, in the session mode. Please note that file Chart.yaml must exist in the directory. -git clone https://github.com/stefanprodan/podinfo -cd podinfo -gitops beta run ./charts/podinfo --timeout 3m --port-forward namespace=flux-system,resource=svc/run-dev-helm-podinfo,port=9898:9898`, - SilenceUsage: true, - SilenceErrors: true, - PreRunE: betaRunCommandPreRunE(&opts.Endpoint), - RunE: betaRunCommandRunE(opts), - DisableAutoGenTag: true, - } - - cmdFlags := cmd.Flags() - - cmdFlags.StringVar(&flags.FluxVersion, "flux-version", version.FluxVersion, "The version of Flux to install.") - cmdFlags.StringSliceVar(&flags.AllowK8sContext, "allow-k8s-context", []string{}, "The name of the KubeConfig context to explicitly allow.") - cmdFlags.StringSliceVar(&flags.Components, "components", []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}, "The Flux components to install.") - cmdFlags.StringSliceVar(&flags.ComponentsExtra, "components-extra", []string{}, "Additional Flux components to install, allowed values are image-reflector-controller,image-automation-controller.") - cmdFlags.DurationVar(&flags.Timeout, "timeout", 5*time.Minute, "The timeout for operations during GitOps Run.") - cmdFlags.StringVar(&flags.PortForward, "port-forward", "", "Forward the port from a cluster's resource to your local machine i.e. 'port=8080:8080,resource=svc/app'.") - cmdFlags.StringVar(&flags.DashboardPort, "dashboard-port", "9001", "GitOps Dashboard port") - cmdFlags.BoolVar(&flags.SkipDashboardInstall, "skip-dashboard-install", false, "Skip installation of the Dashboard. This also disables the prompt asking whether the Dashboard should be installed.") - cmdFlags.StringVar(&flags.DashboardHashedPassword, "dashboard-hashed-password", "", "GitOps Dashboard password in BCrypt hash format") - cmdFlags.StringVar(&flags.RootDir, "root-dir", "", "Specify the root directory to watch for changes. If not specified, the root of Git repository will be used.") - cmdFlags.StringVar(&flags.SessionName, "session-name", getSessionNameFromGit(), "Specify the name of the session. If not specified, the name of the current branch and the last commit id will be used.") - cmdFlags.StringVar(&flags.SessionNamespace, "session-namespace", "default", "Specify the namespace of the session.") - cmdFlags.BoolVar(&flags.NoSession, "no-session", false, "Disable session management. If not specified, the session will be enabled by default.") - cmdFlags.BoolVar(&flags.NoBootstrap, "no-bootstrap", false, "Disable bootstrapping at shutdown.") - cmdFlags.BoolVar(&flags.SkipResourceCleanup, "skip-resource-cleanup", false, "Skip resource cleanup. If not specified, the GitOps Run resources will be deleted by default.") - cmdFlags.StringVar(&flags.DecryptionKeyFile, "decryption-key-file", "", "Path to an age key file used for decrypting Secrets using SOPS.") - - cmdFlags.StringSliceVar(&flags.DashboardValuesFiles, "values", nil, "Local path to values.yaml files for HelmRelease, also accepts comma-separated values.") - cmdFlags.StringVar(&flags.DashboardImage, "dashboard-image", "", "Override GitOps Dashboard image") - _ = cmdFlags.MarkHidden("dashboard-image") - - cmdFlags.StringVar(&flags.HiddenSessionName, "x-session-name", "", "The session name acknowledged by the sub-process. This is a hidden flag and should not be used.") - _ = cmdFlags.MarkHidden("x-session-name") - - kubeConfigArgs = run.GetKubeConfigArgs() - - kubeConfigArgs.AddFlags(cmd.Flags()) - - return cmd -} - -func getSessionNameFromGit() string { - const prefix = "run" - - branch, err := run.GetBranchName() - if err != nil { - return "" - } - - commit, err := run.GetCommitID() - if err != nil { - return "" - } - - isDirty, err := run.IsDirty() - if err != nil { - return "" - } - - sessionName := fmt.Sprintf("%s-%s-%s", prefix, branch, commit) - if isDirty { - sessionName = fmt.Sprintf("%s-%s-%s-dirty", prefix, branch, commit) - } - - sessionName = strings.ToLower(strings.ReplaceAll(sessionName, "/", "-")) - - return sessionName -} - -func betaRunCommandPreRunE(endpoint *string) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - numArgs := len(args) - - if numArgs == 0 { - return cmderrors.ErrNoFilePath - } - - if numArgs > 1 { - return cmderrors.ErrMultipleFilePaths - } - - return nil - } -} - -func getKubeClient(cmd *cobra.Command) (*kube.KubeHTTP, *rest.Config, error) { - var err error - - log := logger.NewCLILogger(os.Stdout) - - if flags.Namespace, err = cmd.Flags().GetString("namespace"); err != nil { - return nil, nil, err - } - - kubeConfigArgs.Namespace = &flags.Namespace - - if flags.KubeConfig, err = cmd.Flags().GetString("kubeconfig"); err != nil { - return nil, nil, err - } - - if flags.Context, err = cmd.Flags().GetString("context"); err != nil { - return nil, nil, err - } - - if flags.KubeConfig != "" { - kubeConfigArgs.KubeConfig = &flags.KubeConfig - - if flags.Context == "" { - log.Failuref("A context should be provided if a kubeconfig is provided") - return nil, nil, cmderrors.ErrNoContextForKubeConfig - } - } - - log.Actionf("Checking for a cluster in the kube config ...") - - var contextName string - - if flags.Context != "" { - contextName = flags.Context - } else { - _, contextName, err = kube.RestConfig() - if err != nil { - log.Failuref("Error getting a restconfig: %v", err.Error()) - return nil, nil, cmderrors.ErrNoCluster - } - } - - cfg, err := kubeConfigArgs.ToRESTConfig() - if err != nil { - return nil, nil, fmt.Errorf("error getting a restconfig from kube config args: %w", err) - } - - kubeClientOpts := run.GetKubeClientOptions() - kubeClientOpts.BindFlags(cmd.Flags()) - - kubeClient, err := run.GetKubeClient(log, contextName, cfg, kubeClientOpts) - if err != nil { - return nil, nil, cmderrors.ErrGetKubeClient - } - - return kubeClient, cfg, nil -} - -func fluxStep(log logger.Logger, kubeClient *kube.KubeHTTP) (fluxVersion *install.FluxVersionInfo, justInstalled bool, err error) { - ctx := context.Background() - - log.Actionf("Checking if Flux is already installed ...") - - guessed := false - if fluxVersion, guessed, err = install.GetFluxVersion(ctx, log, kubeClient); err != nil { - log.Warningf("Flux is not found: %v", err.Error()) - - product := fluxinstall.NewProduct(flags.FluxVersion) - - installer := fluxinstall.NewInstaller() - - execPath, err := installer.Ensure(ctx, product) - if err != nil { - execPath, err = installer.Install(ctx, product) - if err != nil { - return nil, false, err - } - } - - wd, err := os.Getwd() - if err != nil { - return nil, false, err - } - - flux, err := fluxexec.NewFlux(wd, execPath) - if err != nil { - return nil, false, err - } - - // This means that Flux logs will be printed to the console, but not be sent to S3 - flux.SetLogger(log.L()) - - var components []fluxexec.Component - for _, component := range flags.Components { - components = append(components, fluxexec.Component(component)) - } - - var componentsExtra []fluxexec.ComponentExtra - for _, component := range flags.ComponentsExtra { - componentsExtra = append(componentsExtra, fluxexec.ComponentExtra(component)) - } - - if err := flux.Install(ctx, - fluxexec.Components(components...), - fluxexec.ComponentsExtra(componentsExtra...), - fluxexec.WithGlobalOptions( - fluxexec.Namespace(flags.Namespace), - fluxexec.Timeout(flags.Timeout), - ), - ); err != nil { - return nil, false, err - } - - return &install.FluxVersionInfo{ - FluxVersion: flags.FluxVersion, - FluxNamespace: flags.Namespace, - }, true, nil - } else { - if guessed { - log.Warningf("Flux version could not be determined, assuming %s and namespace %s by mapping from the version of the Source controller %s", fluxVersion.FluxVersion, fluxVersion.FluxNamespace, fluxVersion.SourceControllerVersion) - } else { - log.Successf("Flux %s is already installed on the %s namespace.", fluxVersion.FluxVersion, fluxVersion.FluxNamespace) - } - } - - return fluxVersion, false, nil -} - -// fluentBitStep installs Fluent Bit on the cluster and returns a CleanupFunc to remove it again. The function -// ascertains that Fluent Bit is ready before returning. -func fluentBitStep(ctx context.Context, log logger.Logger, kubeClient *kube.KubeHTTP, devBucketHTTPPort int32) (CleanupFunc, error) { - err := install.InstallFluentBit(ctx, log, kubeClient, flags.Namespace, constants.GitOpsRunNamespace, install.FluentBitHRName, logger.PodLogBucketName, devBucketHTTPPort) - - if err != nil { - log.Failuref("Fluent Bit installation failed: %v", err.Error()) - return nil, err - } - - return func(ctx context.Context, log0 logger.Logger) error { - return install.UninstallFluentBit(ctx, log0, kubeClient, flags.Namespace, install.FluentBitHRName) - }, nil -} - -func dashboardStep(ctx context.Context, log logger.Logger, kubeClient *kube.KubeHTTP, generateManifestsOnly bool, dashboardHashedPassword string, dashboardValuesFiles []string) (install.DashboardType, []byte, string, error) { - log.Actionf("Checking if GitOps Dashboard is already installed ...") - - dashboardType, dashboardName, err := install.GetInstalledDashboard(ctx, kubeClient, flags.Namespace, map[install.DashboardType]bool{ - install.DashboardTypeOSS: true, install.DashboardTypeEnterprise: true, - }) - if err != nil { - return dashboardType, nil, "", fmt.Errorf("error getting installed dashboard: %w", err) - } - - shouldReconcileDashboard := false - var dashboardManifests []byte - - switch dashboardType { - case install.DashboardTypeEnterprise: - flags.SkipDashboardInstall = true - log.Warningf("GitOps Enterprise Dashboard was found. GitOps OSS Dashboard will not be installed") - return dashboardType, nil, "", err - case install.DashboardTypeOSS: - log.Warningf("GitOps Dashboard was found") - return dashboardType, nil, "", err - default: - wantToInstallTheDashboard := false - if dashboardHashedPassword != "" { - wantToInstallTheDashboard = true - } else if !flags.SkipDashboardInstall && dashboardHashedPassword == "" { - prompt := promptui.Prompt{ - Label: "Would you like to install the GitOps Dashboard", - IsConfirm: true, - Default: "Y", - } - - // Answering "n" causes err to not be nil. Hitting enter without typing - // does not return the default. - answer, err := prompt.Run() - if err == nil { - wantToInstallTheDashboard = true - } else if answer == "n" || answer == "N" { - wantToInstallTheDashboard = false - flags.SkipDashboardInstall = true - } - } - - if !wantToInstallTheDashboard { - break - } - - passwordHash := "" - if dashboardHashedPassword == "" { - password, err := install.ReadPassword(log) - if err != nil { - return install.DashboardTypeNone, nil, "", err - } - - if password == "" { - return install.DashboardTypeNone, nil, "", fmt.Errorf("dashboard password is an empty string") - } - - passwordHash, err = install.GeneratePasswordHash(log, password) - if err != nil { - return install.DashboardTypeNone, nil, "", err - } - } else { - passwordHash = dashboardHashedPassword - } - - dashboardObjects, err := install.CreateDashboardObjects(log, defaultDashboardName, flags.Namespace, adminUsername, passwordHash, HelmChartVersion, flags.DashboardImage, dashboardValuesFiles) - if err != nil { - return install.DashboardTypeNone, nil, "", fmt.Errorf("error creating dashboard objects: %w", err) - } - - if generateManifestsOnly { - return install.DashboardTypeNone, dashboardObjects.Manifests, passwordHash, nil - } - - if err := install.InstallDashboard(ctx, log, kubeClient, dashboardObjects); err != nil { - return install.DashboardTypeNone, nil, "", fmt.Errorf("gitops dashboard installation failed: %w", err) - } else { - dashboardType = install.DashboardTypeOSS - dashboardName = defaultDashboardName - shouldReconcileDashboard = true - - log.Successf("GitOps Dashboard has been installed") - } - } - - if dashboardType == install.DashboardTypeOSS && shouldReconcileDashboard { - log.Actionf("Request reconciliation of dashboard (timeout %v) ...", flags.Timeout) - - if dashboardName == "" { - dashboardName = defaultDashboardName - } - - if err := install.ReconcileDashboard(ctx, kubeClient, dashboardName, flags.Namespace, dashboardPodName, flags.Timeout); err != nil { - log.Failuref("Error requesting reconciliation of dashboard: %v", err.Error()) - return install.DashboardTypeNone, nil, "", err - } else { - log.Successf("Dashboard reconciliation is done.") - } - } - - return dashboardType, dashboardManifests, "", nil -} - -func runCommandOuterProcess(cmd *cobra.Command, args []string) (retErr error) { - paths, err := run.NewPaths(args[0], flags.RootDir) - if err != nil { - return err - } - - kubeClient, _, err := getKubeClient(cmd) - if err != nil { - return err - } - - // create session - sessionLog := logger.NewCLILogger(os.Stdout) - sessionLog.Actionf("Preparing the cluster for GitOps Run session ...\n") - - sessionLog.Println("You can run `gitops beta run --no-session` to disable session management.\n") - - sessionLog.Println("If you are running GitOps Run for the first time, it may take a few minutes to download the required images.") - sessionLog.Println("GitOps Run session is also required to install Flux components, if it is not installed yet.") - sessionLog.Println("You may see Flux installation logs in the next step.\n") - - // showing Flux installation twice is confusing - log := logger.NewCLILogger(io.Discard) - - var ( - fluxJustInstalled bool - fluxVersionInfo *install.FluxVersionInfo - ) - - if fluxVersionInfo, fluxJustInstalled, err = fluxStep(log, kubeClient); err != nil { - return fmt.Errorf("failed to detect or install Flux on the host cluster: %v", err) - } - - dashboardType, dashboardManifests, dashboardHashedPassword, err := dashboardStep(context.Background(), log, kubeClient, true, flags.DashboardHashedPassword, flags.DashboardValuesFiles) - if err != nil && !errors.Is(err, install.ErrDashboardInstalled) { - return fmt.Errorf("failed to generate dashboard manifests: %v", err) - } - - sessionLog.Actionf("Creating GitOps Run session %s in namespace %s ...", flags.SessionName, flags.SessionNamespace) - - sessionLog.Println("\nYou may see Flux installation logs again, as it is being installed inside the session.\n") - - portForwardsForSession := []string{} - if dashboardType == install.DashboardTypeOSS && err == nil { - portForwardsForSession = append(portForwardsForSession, flags.DashboardPort) - } - - if flags.PortForward != "" { - spec, err := watch.ParsePortForwardSpec(flags.PortForward) - if err != nil { - return err - } - - portForwardsForSession = append(portForwardsForSession, spec.HostPort) - } - - var kind string - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - kind = "helm" - } else if !yes && err == nil { - kind = "ks" - } else { - return err - } - - session, err := install.NewSession( - sessionLog, - kubeClient, - flags.SessionName, - flags.SessionNamespace, - fluxVersionInfo.FluxNamespace, // flux namespace of the session - portForwardsForSession, - flags.SkipDashboardInstall, - dashboardHashedPassword, - kind, - ) - - if err != nil { - return err - } - - sessionLog.Actionf("Waiting for GitOps Run session %s to be ready ...", flags.SessionName) - - if err := session.Start(); err != nil { - return err - } - - sessionLog.Successf("Session %s is ready", flags.SessionName) - - sessionLog.Actionf("Connecting to GitOps Run session %s ...", flags.SessionName) - - if err := session.Connect(); err != nil { - return err - } - - sessionLog.Println("") - sessionLog.Actionf("Deleting GitOps Run session %s ...", flags.SessionName) - - if err := session.Close(); err != nil { - sessionLog.Failuref("Failed to delete session %s: %v", flags.SessionName, err) - return err - } else { - sessionLog.Successf("Session %s is deleted successfully", flags.SessionName) - } - - // now that the session is deleted, we return to the host cluster - - // run bootstrap wizard only if Flux was not installed - if fluxJustInstalled && !flags.NoBootstrap { - prompt := promptui.Prompt{ - Label: "Would you like to bootstrap your cluster into GitOps mode", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - - for { - err := runBootstrap(context.Background(), log, paths, dashboardManifests) - if err == nil { - break - } - - log.Warningf("Error bootstrapping: %v", err) - - prompt := promptui.Prompt{ - Label: "Couldn't bootstrap - would you like to try again", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - } - } - - return err -} - -func runCommandInnerProcess(cmd *cobra.Command, args []string) error { - // There are two loggers in this function. - // 1. log0 is the os.Stdout logger - // 2. log is the S3 logger that also delegates its outputs to "log0". - log0 := logger.NewCLILogger(os.Stdout) - - paths, err := run.NewPaths(args[0], flags.RootDir) - if err != nil { - return err - } - - kubeClient, cfg, err := getKubeClient(cmd) - if err != nil { - return err - } - - discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) - if err != nil { - return err - } - - serverVersion, err := discoveryClient.ServerVersion() - if err != nil { - return err - } - - // We need the server version to pass to the validation package - kubernetesVersion := trimK8sVersion(serverVersion.GitVersion) - - contextName := kubeClient.ClusterName - validAllowedContext := false - - for _, allowContext := range flags.AllowK8sContext { - if allowContext == contextName { - log0.Actionf("Explicitly allow GitOps Run on %s context", contextName) - - validAllowedContext = true - - break - } - } - - if !validAllowedContext { - if !run.IsLocalCluster(kubeClient) { - return fmt.Errorf("to run against a remote cluster, use --allow-k8s-context=%s", contextName) - } - } - - ctx, cancel := context.WithCancel(context.Background()) - sigs := make(chan os.Signal, 1) - // Subscribe to SIGUSR1 in addition to the usual signals because in session mode - // the outer process traps SIGTERM and SIGINT and sends SIGUSR1 to the inner process. - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - cleanupFns := CleanupFuncs{} - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancel() - return nil - }) - - // Using defer instead - // if CleanupCluster already called during the process, the stack will be empty and nothing will happen. - defer func(ctx context.Context, log logger.Logger, fns CleanupFuncs) { - err := CleanupCluster(ctx, log, fns) - if err != nil { - log.Failuref("Error cleaning up: %v", err) - } - }(ctx, log0 /*logger.NewCLILogger(io.Discard)*/, cleanupFns) - - go func() { - select { - case <-ctx.Done(): - case sig := <-sigs: - log0.Actionf("Received %s, quitting...", sig) - // re-enable listening for ctrl+C - signal.Reset(sig) - cancel() - return - } - }() - - var ( - fluxJustInstalled bool - fluxVersionInfo *install.FluxVersionInfo - ) - - fluxVersionInfo, fluxJustInstalled, err = fluxStep(log0, kubeClient) - if err != nil { - return err - } - - sessionName := flags.HiddenSessionName - if sessionName == "" { - sessionName = "no-session" - } - - var username string - if current, err := user.Current(); err != nil { - username = "unknown" - } else { - username = current.Username - } - - // ====================== Dev-bucket ====================== - // Install dev-bucket server before everything, so that we can also forward logs to it - unusedPorts, err := run.GetUnusedPorts(2) - if err != nil { - return err - } - - devBucketHTTPPort := unusedPorts[0] - devBucketHTTPSPort := unusedPorts[1] - - // generate access key and secret key for Minio auth - accessKey, err := s3.GenerateAccessKey(s3.DefaultRandIntFunc) - if err != nil { - return fmt.Errorf("failed generating access key: %w", err) - } - - secretKey, err := s3.GenerateSecretKey(s3.DefaultRandIntFunc) - if err != nil { - return fmt.Errorf("failed generating secret key: %w", err) - } - - cancelDevBucketPortForwarding, cert, err := watch.InstallDevBucketServer(ctx, log0, kubeClient, cfg, devBucketHTTPPort, devBucketHTTPSPort, accessKey, secretKey) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancelDevBucketPortForwarding() - return watch.UninstallDevBucketServer(ctx, log, kubeClient) - }) - if err != nil { - return fmt.Errorf("unable to install S3 bucket server: %w", err) - } - - minioClient, err := s3.NewMinioClient(fmt.Sprintf("localhost:%d", devBucketHTTPSPort), accessKey, secretKey, cert) - if err != nil { - return err - } - - if err := logger.CreateBucket(minioClient, logger.SessionLogBucketName); err != nil { - return err - } - - if err := logger.CreateBucket(minioClient, logger.PodLogBucketName); err != nil { - return err - } - - log, err := logger.NewS3LogWriter(minioClient, sessionName, log0) - if err != nil { - return fmt.Errorf("failed creating S3 log writer: %w", err) - } - - // ====================== Fluent-Bit ===================== - fbCleanupFn, err := fluentBitStep(ctx, log, kubeClient, devBucketHTTPPort) - cleanupFns.Push(fbCleanupFn) - if err != nil { - return err - } - - // ====================== Dashboard ====================== - var ( - dashboardType install.DashboardType - dashboardErr error - dashboardManifests []byte - ) - - dashboardType, dashboardManifests, _, dashboardErr = dashboardStep(ctx, log, kubeClient, false, flags.DashboardHashedPassword, flags.DashboardValuesFiles) - if err != nil && !errors.Is(err, install.ErrDashboardInstalled) { - cancel() - return err - } - - var cancelDashboardPortForwarding func() = nil - - if dashboardType == install.DashboardTypeOSS && dashboardErr == nil { - cancelDashboardPortForwarding, err = watch.EnablePortForwardingForDashboard(ctx, log, kubeClient, cfg, flags.Namespace, dashboardPodName, flags.DashboardPort) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - cancelDashboardPortForwarding() - return nil - }) - if err != nil { - return err - } - } - - if err := watch.InitializeRootDir(log, paths.RootDir); err != nil && !errors.Is(err, sourceignore.ErrIgnoreFileExists) { - return fmt.Errorf("couldn't initialize root dir %s: %w", paths.RootDir, err) - } - - if err := watch.InitializeTargetDir(paths.GetAbsoluteTargetDir()); err != nil { - return fmt.Errorf("couldn't set up against target %s: %w", paths.TargetDir, err) - } - - setupParams := watch.SetupRunObjectParams{ - Namespace: flags.Namespace, - Path: paths.TargetDir, - Timeout: flags.Timeout, - DevBucketPort: devBucketHTTPPort, - SessionName: sessionName, - Username: username, - AccessKey: accessKey, - SecretKey: secretKey, - DecryptionKeyFile: flags.DecryptionKeyFile, - } - - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - err := watch.SetupBucketSourceAndHelm(ctx, log, kubeClient, setupParams) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - if !flags.SkipResourceCleanup { - return watch.CleanupBucketSourceAndHelm(ctx, log, kubeClient, flags.Namespace) - } - return nil - }) - if err != nil { - return err - } - } else if !yes && err == nil { - err := watch.SetupBucketSourceAndKS(ctx, log, kubeClient, setupParams) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - if !flags.SkipResourceCleanup { - return watch.CleanupBucketSourceAndKS(ctx, log, kubeClient, flags.Namespace) - } - return nil - }) - if err != nil { - return err - } - } else if err != nil { - log.Actionf("Unable to determine if target is a Helm or Kustomization directory: %v", err) - return err - } - - minioClient, err = s3.NewMinioClient(fmt.Sprintf("localhost:%d", devBucketHTTPSPort), accessKey, secretKey, cert) - if err != nil { - return err - } - - // watch for file changes in dir gitRepoRoot - watcher, err := fsnotify.NewWatcher() - if err != nil { - return err - } - - ignorer := watch.CreateIgnorer(paths.RootDir) - - err = filepath.Walk(paths.RootDir, watch.WatchDirsForFileWalker(watcher, ignorer)) - if err != nil { - return err - } - - // cancel function to stop forwarding port - var ( - cancelPortFwd func() - // atomic counter for the number of file change events that have changed - counter uint64 = 1 - needToRescan = false - ) - - watcherCtx, watcherCancel := context.WithCancel(ctx) - lastReconcile := time.Now() - closeOnce := sync.Once{} - stopUploadCh := make(chan struct{}) - cleanupFns.Push(func(ctx context.Context, log logger.Logger) error { - watcherCancel() - closeOnce.Do(func() { - close(stopUploadCh) - }) - return nil - }) - - go func() { - for { - select { - case <-watcherCtx.Done(): - return - case <-stopUploadCh: - return - case event := <-watcher.Events: - info, err := os.Stat(event.Name) - if err != nil { - continue - } - if event.Op&fsnotify.Create == fsnotify.Create || - event.Op&fsnotify.Remove == fsnotify.Remove || - event.Op&fsnotify.Rename == fsnotify.Rename { - // if it's a dir, we need to watch it - if info.IsDir() { - needToRescan = true - } - } - - // Skip all dotfiles because these are usually created by editors as swap files. A reconciliation - // should only be triggered by files that are actually part of the application being run. - base := filepath.Base(event.Name) - - if !info.IsDir() && strings.HasPrefix(base, ".") && base != sourceignore.IgnoreFilename { - continue - } - - if cancelPortFwd != nil { - cancelPortFwd() - } - - // If there are still changes and it's been a few seconds, - // cancel the old context and start over. - if time.Since(lastReconcile) > (10 * time.Second) { - watcherCancel() - watcherCtx, watcherCancel = context.WithCancel(ctx) - } - - atomic.AddUint64(&counter, 1) - case err := <-watcher.Errors: - if err != nil { - log.Failuref("Error: %v", err) - } - } - } - }() - - // event aggregation loop - ticker := time.NewTicker(680 * time.Millisecond) - - go func() { - for { //nolint:gosimple - select { - case <-stopUploadCh: - return - case <-ticker.C: - if counter > 0 { - log.Actionf("%d change events detected", counter) - - // reset counter - atomic.StoreUint64(&counter, 0) - - // we have to skip validation for helm charts - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); !yes && err == nil { - // validate only files under the target dir - log.Actionf("Validating files under %s/ ...", paths.TargetDir) - - if err := validate.Validate(log, paths.GetAbsoluteTargetDir(), paths.RootDir, kubernetesVersion, fluxVersionInfo.FluxVersion); err != nil { - log.Failuref("Validation failed: please review the errors and try again: %v", err) - continue - } - } - - // use ctx, not thisCtx - incomplete uploads will never make anybody happy - if err := watch.SyncDir(ctx, log, paths.RootDir, constants.RunDevBucketName, minioClient, ignorer); err != nil { - log.Failuref("Error syncing dir: %v", err) - } - - if needToRescan { - // close the old watcher - if err := watcher.Close(); err != nil { - log.Warningf("Error closing the old watcher: %v", err) - } - // create a new watcher - watcher, err = fsnotify.NewWatcher() - if err != nil { - log.Failuref("Error creating new watcher: %v", err) - } - - err = filepath.Walk(paths.RootDir, watch.WatchDirsForFileWalker(watcher, ignorer)) - if err != nil { - log.Failuref("Error re-walking dir: %v", err) - } - - needToRescan = false - } - - log.Actionf("Request reconciliation of GitOps Run resources (timeout %v) ... ", flags.Timeout) - - lastReconcile = time.Now() - // context that cancels when files change - thisCtx := watcherCtx - - var reconcileErr error - if yes, err := isHelm(paths.GetAbsoluteTargetDir()); yes && err == nil { - reconcileErr = watch.ReconcileDevBucketSourceAndHelm(thisCtx, log, kubeClient, flags.Namespace, flags.Timeout) - } else if !yes && err == nil { - reconcileErr = watch.ReconcileDevBucketSourceAndKS(thisCtx, log, kubeClient, flags.Namespace, flags.Timeout) - } else if err != nil { - log.Actionf("Unable to determine if target is a Helm or Kustomization directory: %v", err) - reconcileErr = err - } - - if reconcileErr != nil { - if errors.Is(reconcileErr, context.Canceled) { - log.Actionf("Context canceled, skipping reconciliation.") - } else { - log.Failuref("Error requesting reconciliation: %v", reconcileErr) - } - } else { - log.Successf("Reconciliation is done.") - } - - portForwards := map[rune]watch.PortForwardShortcut{} - - if dashboardType == install.DashboardTypeOSS && dashboardErr == nil { - portForwardKey, err := watch.GetNextPortForwardKey(portForwards) - if err != nil { - log.Failuref("Error adding a portForward: %v", err) - } else { - portForwards[portForwardKey] = watch.PortForwardShortcut{ - Name: defaultDashboardName, - HostPort: flags.DashboardPort, - } - } - } - - var specMap *watch.PortForwardSpec - - if flags.PortForward != "" { - specMap, err = watch.ParsePortForwardSpec(flags.PortForward) - if err != nil { - log.Failuref("Error parsing port forward spec: %v", err) - } else { - serviceName := specMap.Name - if serviceName == "" { - serviceName = "service" - } - - portForwardKey, err := watch.GetNextPortForwardKey(portForwards) - if err != nil { - log.Failuref("Error adding a port forward: %v", err) - } else { - portForwards[portForwardKey] = watch.PortForwardShortcut{ - Name: serviceName, - HostPort: specMap.HostPort, - } - } - } - } - - if len(portForwards) > 0 { - watch.ShowPortForwards(ctx, log, portForwards) - } - - if specMap != nil { - // get pod from specMap - namespacedName := types.NamespacedName{Namespace: specMap.Namespace, Name: specMap.Name} - - var ( - pod *corev1.Pod - podErr error - ) - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - if pollErr := wait.PollImmediate(2*time.Second, flags.Timeout, func() (bool, error) { - pod, podErr = run.GetPodFromResourceDescription(thisCtx, kubeClient, namespacedName, specMap.Kind, nil) - if pod != nil && podErr == nil { - return true, nil - } - - log.Waitingf("Waiting for a pod from specMap: %v", podErr) - return false, nil - }); pollErr != nil { - log.Failuref("Waiting for a pod from specMap: %v", pollErr) - } - - if pod == nil { - log.Failuref("Error getting pod from specMap") - } else /* pod is available */ { - waitFwd := make(chan struct{}, 1) - readyChannel := make(chan struct{}) - cancelPortFwd = func() { - close(waitFwd) - - cancelPortFwd = nil - } - - log.Actionf("Port forwarding to pod %s/%s ...", pod.Namespace, pod.Name) - - // this function _BLOCKS_ until the stopChannel (waitPwd) is closed. - if err := watch.ForwardPort(log.L(), pod, cfg, specMap, waitFwd, readyChannel); err != nil { - log.Failuref("Error forwarding port: %v", err) - } - - log.Successf("Port forwarding is stopped.") - } - } - } - } - } - }() - - // wait for interrupt or ctrl+C - log.Waitingf("Press Ctrl+C to stop GitOps Run ...") - - <-ctx.Done() - - closeOnce.Do(func() { - close(stopUploadCh) - }) - - if err := watcher.Close(); err != nil { - log.Warningf("Error closing watcher: %v", err.Error()) - } - - // print a blank line to make it easier to read the logs - fmt.Println() - - if cancelDashboardPortForwarding != nil { - cancelDashboardPortForwarding() - } - - ticker.Stop() - - // this is the default behaviour - if !flags.SkipResourceCleanup { - // create new context that isn't cancelled, for cleanup and bootstrapping - ctx = context.Background() - if err := CleanupCluster(ctx, log0, cleanupFns); err != nil { - return fmt.Errorf("failed cleaning up: %w", err) - } - } - - // run bootstrap wizard only if Flux was not installed - if fluxJustInstalled && !flags.NoBootstrap { - prompt := promptui.Prompt{ - Label: "Would you like to bootstrap your cluster into GitOps mode", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - - for { - err := runBootstrap(ctx, log0, paths, dashboardManifests) - if err == nil { - break - } - - log0.Warningf("Error bootstrapping: %v", err) - - prompt := promptui.Prompt{ - Label: "Couldn't bootstrap - would you like to try again", - IsConfirm: true, - Default: "Y", - } - - _, err = prompt.Run() - if err != nil { - return nil - } - } - } - - return nil -} - -func isHelm(dir string) (bool, error) { - _, err := os.Stat(filepath.Join(dir, "Chart.yaml")) - if err != nil && os.IsNotExist(err) { - // check Chart.yml - _, err = os.Stat(filepath.Join(dir, "Chart.yml")) - if err != nil && os.IsNotExist(err) { - return false, nil - } else if err != nil { - return false, err - } - } else if err != nil { - return false, err - } - - return true, nil -} - -func runBootstrap(ctx context.Context, log logger.Logger, paths *run.Paths, manifests []byte) (err error) { - // parse remote - repo, err := bootstrap.ParseGitRemote(log, paths.RootDir) - if err != nil { - log.Failuref("Error parsing Git remote: %v", err.Error()) - } - - // run the bootstrap wizard - log.Actionf("Starting bootstrap wizard ...") - - host := bootstrap.GetHost(repo) - gitProvider := bootstrap.ParseGitProvider(host) - - log.Waitingf("Press Ctrl+C to stop bootstrap wizard ...") - - if gitProvider == bootstrap.GitProviderUnknown { - gitProvider, err = bootstrap.SelectGitProvider(log) - if err != nil { - log.Failuref("Error selecting git provider: %v", err.Error()) - } - } - - wizard, err := bootstrap.NewBootstrapWizard(log, gitProvider, repo) - - if err != nil { - return fmt.Errorf("error creating bootstrap wizard: %v", err.Error()) - } - - if err = wizard.Run(log); err != nil { - return fmt.Errorf("error running bootstrap wizard: %v", err.Error()) - } - - params := wizard.BuildCmd(log) - - product := fluxinstall.NewProduct(flags.FluxVersion) - - installer := fluxinstall.NewInstaller() - - execPath, err := installer.Ensure(ctx, product) - if err != nil { - execPath, err = installer.Install(ctx, product) - if err != nil { - return err - } - } - - wd, err := os.Getwd() - if err != nil { - return err - } - - flux, err := fluxexec.NewFlux(wd, execPath) - if err != nil { - return err - } - - flux.SetLogger(log.L()) - - slugifiedWorkloadPath := strings.ReplaceAll(paths.TargetDir, "/", "-") - - workloadKustomizationPath := strings.Join([]string{paths.ClusterDir, slugifiedWorkloadPath, slugifiedWorkloadPath + "-kustomization.yaml"}, "/") - workloadKustomization := kustomizev1.Kustomization{ - TypeMeta: metav1.TypeMeta{ - Kind: kustomizev1.KustomizationKind, - APIVersion: kustomizev1.GroupVersion.Identifier(), - }, - - ObjectMeta: metav1.ObjectMeta{ - Name: slugifiedWorkloadPath, - Namespace: flags.Namespace, - }, - Spec: kustomizev1.KustomizationSpec{ - Interval: metav1.Duration{Duration: 1 * time.Hour}, - Prune: true, // GC the kustomization - SourceRef: kustomizev1.CrossNamespaceSourceReference{ - Kind: sourcev1.GitRepositoryKind, - Name: "flux-system", - Namespace: "flux-system", - }, - Timeout: &metav1.Duration{Duration: 5 * time.Minute}, - Path: "./" + paths.TargetDir, - Wait: true, - }, - } - - workloadKustomizationContent, err := yaml.Marshal(workloadKustomization) - if err != nil { - return err - } - - workloadKustomizationContent, err = install.SanitizeResourceData(log, workloadKustomizationContent) - if err != nil { - return err - } - - workloadKustomizationContentStr := string(workloadKustomizationContent) - - commitFiles := []gitprovider.CommitFile{{ - Path: &workloadKustomizationPath, - Content: &workloadKustomizationContentStr, - }} - - if len(manifests) > 0 { - strManifests := string(manifests) - dashboardPath := strings.Join([]string{paths.ClusterDir, "weave-gitops", "dashboard.yaml"}, "/") - - commitFiles = append(commitFiles, gitprovider.CommitFile{ - Path: &dashboardPath, - Content: &strManifests, - }) - } - - err = filepath.WalkDir(paths.GetAbsoluteTargetDir(), func(path string, entry fs.DirEntry, err error) error { - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - if entry.IsDir() { - return nil - } - content, err := os.ReadFile(path) - strContent := string(content) - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - relativePath, err := run.GetRelativePathToRootDir(paths.RootDir, path) - if err != nil { - log.Warningf("Error: %v", err.Error()) - return err - } - commitFiles = append(commitFiles, gitprovider.CommitFile{ - Path: &relativePath, - Content: &strContent, - }) - return nil - }) - if err != nil { - return err - } - - bs := bootstrap.NewBootstrap(paths.ClusterDir, params.Options, params.Provider) - - err = bs.RunBootstrapCmd(ctx, flux) - if err != nil { - return err - } - - err = bs.SyncResources(ctx, log, commitFiles) - if err != nil { - return err - } - - return nil -} - -func betaRunCommandRunE(opts *config.Options) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - if flags.NoSession { - return runCommandInnerProcess(cmd, args) - } else { - return runCommandOuterProcess(cmd, args) - } - } -} diff --git a/cmd/gitops/beta/run/trim_k8s_version.go b/cmd/gitops/beta/run/trim_k8s_version.go deleted file mode 100644 index c302159b92..0000000000 --- a/cmd/gitops/beta/run/trim_k8s_version.go +++ /dev/null @@ -1,14 +0,0 @@ -package run - -import ( - "regexp" - "strings" -) - -func trimK8sVersion(version string) string { - // match v1.18.0 with regex - regex := regexp.MustCompile(`^v?\d+\.\d+\.\d+`) - firstPart := regex.FindString(version) - - return strings.TrimPrefix(firstPart, "v") -} diff --git a/cmd/gitops/beta/run/trim_k9s_version_test.go b/cmd/gitops/beta/run/trim_k9s_version_test.go deleted file mode 100644 index bcb42b0933..0000000000 --- a/cmd/gitops/beta/run/trim_k9s_version_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package run - -import "testing" - -func TestTrimK8sVersion(t *testing.T) { - tests := []struct { - name string - in string - want string - }{ - { - name: "1.18.0", - in: "1.18.0", - want: "1.18.0", - }, - { - name: "v1.18.0", - in: "v1.18.0", - want: "1.18.0", - }, - { - name: "v1.18.0+", - in: "v1.18.0+", - want: "1.18.0", - }, - { - name: "v1.18.0+123", - in: "v1.18.0+123", - want: "1.18.0", - }, - { - name: "v1.18.0+123-abc", - in: "v1.18.0+123-abc", - want: "1.18.0", - }, - { - name: "v1.18.0-abc", - in: "v1.18.0-abc", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123", - in: "v1.18.0-abc+123", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def", - in: "v1.18.0-abc+123-def", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456", - in: "v1.18.0-abc+123-def+456", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456-ghi", - in: "v1.18.0-abc+123-def+456-ghi", - want: "1.18.0", - }, - { - name: "v1.18.0-abc+123-def+456-ghi+jkl", - in: "v1.18.0-abc+123-def+456-ghi+jkl", - want: "1.18.0", - }, - { - name: "1.18.0-abc+123-def+456-ghi+jkl", - in: "1.18.0-abc+123-def+456-ghi+jkl", - want: "1.18.0", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := trimK8sVersion(tt.in); got != tt.want { - t.Errorf("trimK8sVersion() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/cmd/gitops/remove/cmd.go b/cmd/gitops/remove/cmd.go deleted file mode 100644 index 35c666f3b1..0000000000 --- a/cmd/gitops/remove/cmd.go +++ /dev/null @@ -1,18 +0,0 @@ -package remove - -import ( - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/cmd/gitops/remove/run" -) - -func GetCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "remove", - Short: "Remove various components of Weave GitOps", - } - - cmd.AddCommand(run.RunCommand(opts)) - - return cmd -} diff --git a/cmd/gitops/remove/run/cmd.go b/cmd/gitops/remove/run/cmd.go deleted file mode 100644 index 1fb1b1a62b..0000000000 --- a/cmd/gitops/remove/run/cmd.go +++ /dev/null @@ -1,202 +0,0 @@ -package run - -import ( - "context" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/weaveworks/weave-gitops/cmd/gitops/cmderrors" - "github.com/weaveworks/weave-gitops/cmd/gitops/config" - "github.com/weaveworks/weave-gitops/pkg/kube" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/install" - "github.com/weaveworks/weave-gitops/pkg/run/session" - "github.com/weaveworks/weave-gitops/pkg/run/watch" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/rest" -) - -type RunCommandFlags struct { - AllSessions bool - NoSession bool - - // Global flags. - Namespace string - KubeConfig string - - // Flags, created by genericclioptions. - Context string -} - -var flags RunCommandFlags - -var kubeConfigArgs *genericclioptions.ConfigFlags - -func RunCommand(opts *config.Options) *cobra.Command { - cmd := &cobra.Command{ - Use: "run", - Short: "Remove GitOps Run sessions", - Long: "Remove GitOps Run sessions", - Example: ` -# Remove the GitOps Run session "dev-1234" from the "flux-system" namespace -gitops remove run --namespace flux-system dev-1234 - -# Remove all GitOps Run sessions from the default namespace -gitops remove run --all-sessions - -# Remove all GitOps Run sessions from the dev namespace -gitops remove run -n dev --all-sessions - -# Clean up resources from a failed GitOps Run in no session mode -gitops remove run --no-session -`, - PreRunE: removeRunPreRunE(opts), - RunE: removeRunRunE(opts), - - SilenceUsage: true, - SilenceErrors: true, - DisableAutoGenTag: true, - } - - cmdFlags := cmd.Flags() - - cmdFlags.BoolVar(&flags.AllSessions, "all-sessions", false, "Remove all GitOps Run sessions") - cmdFlags.BoolVar(&flags.NoSession, "no-session", false, "Remove all GitOps Run components in the non-session mode") - - kubeConfigArgs = run.GetKubeConfigArgs() - - kubeConfigArgs.AddFlags(cmd.Flags()) - - return cmd -} - -func getKubeClient(cmd *cobra.Command) (*kube.KubeHTTP, *rest.Config, error) { - var err error - - log := logger.NewCLILogger(os.Stdout) - - if flags.Namespace, err = cmd.Flags().GetString("namespace"); err != nil { - return nil, nil, err - } - - kubeConfigArgs.Namespace = &flags.Namespace - - if flags.KubeConfig, err = cmd.Flags().GetString("kubeconfig"); err != nil { - return nil, nil, err - } - - if flags.Context, err = cmd.Flags().GetString("context"); err != nil { - return nil, nil, err - } - - if flags.KubeConfig != "" { - kubeConfigArgs.KubeConfig = &flags.KubeConfig - - if flags.Context == "" { - log.Failuref("A context should be provided if a kubeconfig is provided") - return nil, nil, cmderrors.ErrNoContextForKubeConfig - } - } - - var contextName string - - if flags.Context != "" { - contextName = flags.Context - } else { - _, contextName, err = kube.RestConfig() - if err != nil { - log.Failuref("Error getting a restconfig: %v", err.Error()) - return nil, nil, cmderrors.ErrNoCluster - } - } - - cfg, err := kubeConfigArgs.ToRESTConfig() - if err != nil { - return nil, nil, fmt.Errorf("error getting a restconfig from kube config args: %w", err) - } - - kubeClientOpts := run.GetKubeClientOptions() - kubeClientOpts.BindFlags(cmd.Flags()) - - kubeClient, err := run.GetKubeClient(log, contextName, cfg, kubeClientOpts) - if err != nil { - return nil, nil, cmderrors.ErrGetKubeClient - } - - return kubeClient, cfg, nil -} - -func removeRunPreRunE(opts *config.Options) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - // if flags.NoSession is set, we don't need to check for session name - if flags.NoSession { - return nil - } - - numArgs := len(args) - if numArgs == 0 && !flags.AllSessions { - return cmderrors.ErrSessionNameIsRequired - } - - return nil - } -} - -func removeRunRunE(opts *config.Options) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - kubeClient, _, err := getKubeClient(cmd) - if err != nil { - return err - } - - log := logger.NewCLILogger(os.Stdout) - ctx := context.Background() - - if flags.NoSession { - if err := watch.CleanupBucketSourceAndHelm(ctx, log, kubeClient, flags.Namespace); err != nil { - return err - } - - if err := watch.CleanupBucketSourceAndKS(ctx, log, kubeClient, flags.Namespace); err != nil { - return err - } - - if err := watch.UninstallDevBucketServer(ctx, log, kubeClient); err != nil { - return err - } - - if err := install.UninstallFluentBit(ctx, log, kubeClient, flags.Namespace, install.FluentBitHRName); err != nil { - return err - } - } else if flags.AllSessions { - internalSessions, listErr := session.List(kubeClient, flags.Namespace) - if listErr != nil { - return listErr - } - - for _, internalSession := range internalSessions { - log.Actionf("Removing session %s/%s ...", internalSession.SessionNamespace, internalSession.SessionName) - - if err := session.Remove(kubeClient, internalSession); err != nil { - return err - } - - log.Successf("Session %s/%s was successfully removed.", internalSession.SessionNamespace, internalSession.SessionName) - } - } else { - internalSession, err := session.Get(kubeClient, args[0], flags.Namespace) - if err != nil { - return err - } - log.Actionf("Removing session %s/%s ...", internalSession.SessionNamespace, internalSession.SessionName) - if err := session.Remove(kubeClient, internalSession); err != nil { - return err - } - log.Successf("Session %s/%s was successfully removed.", internalSession.SessionNamespace, internalSession.SessionName) - } - - return nil - } -} diff --git a/cmd/gitops/root/cmd.go b/cmd/gitops/root/cmd.go index 1161b5fd68..cc7cc4f42c 100644 --- a/cmd/gitops/root/cmd.go +++ b/cmd/gitops/root/cmd.go @@ -7,25 +7,20 @@ import ( "strings" "time" - "github.com/weaveworks/weave-gitops/cmd/gitops/logs" - "github.com/weaveworks/weave-gitops/cmd/gitops/replan" - "github.com/weaveworks/weave-gitops/cmd/gitops/resume" - "github.com/weaveworks/weave-gitops/cmd/gitops/suspend" - - "github.com/weaveworks/weave-gitops/cmd/gitops/remove" - "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta" - "github.com/weaveworks/weave-gitops/cmd/gitops/beta/run" "github.com/weaveworks/weave-gitops/cmd/gitops/check" cfg "github.com/weaveworks/weave-gitops/cmd/gitops/config" "github.com/weaveworks/weave-gitops/cmd/gitops/create" deletepkg "github.com/weaveworks/weave-gitops/cmd/gitops/delete" "github.com/weaveworks/weave-gitops/cmd/gitops/docs" "github.com/weaveworks/weave-gitops/cmd/gitops/get" + "github.com/weaveworks/weave-gitops/cmd/gitops/logs" + "github.com/weaveworks/weave-gitops/cmd/gitops/replan" + "github.com/weaveworks/weave-gitops/cmd/gitops/resume" "github.com/weaveworks/weave-gitops/cmd/gitops/set" + "github.com/weaveworks/weave-gitops/cmd/gitops/suspend" "github.com/weaveworks/weave-gitops/cmd/gitops/version" "github.com/weaveworks/weave-gitops/pkg/analytics" "github.com/weaveworks/weave-gitops/pkg/config" @@ -159,14 +154,11 @@ func RootCmd() *cobra.Command { rootCmd.AddCommand(set.SetCommand(options)) rootCmd.AddCommand(docs.Cmd) rootCmd.AddCommand(check.Cmd) - rootCmd.AddCommand(beta.GetCommand(options)) rootCmd.AddCommand(create.GetCommand(options)) rootCmd.AddCommand(deletepkg.GetCommand(options)) rootCmd.AddCommand(logs.GetCommand(options)) - rootCmd.AddCommand(remove.GetCommand(options)) rootCmd.AddCommand(replan.Command(options)) rootCmd.AddCommand(resume.Command(options)) - rootCmd.AddCommand(run.RunCommand(options)) rootCmd.AddCommand(suspend.Command(options)) return rootCmd From 3ce297b3220a9a7b2198c8df45ad260f199c659f Mon Sep 17 00:00:00 2001 From: opudrovs Date: Tue, 31 Oct 2023 14:55:11 +0100 Subject: [PATCH 11/23] Replace the Sync/Suspend/Resume controls, used in the `SyncActions` and `CheckboxActions` components, with the new Sync/Suspend/Resume controls (the `SyncControl` component) (#4080) * Create the new `SyncControls` component for Sync/Suspend/Resume controls. * Move all components, related to syncing and suspending objects (existing `SyncActions` and `CheckboxActions` and new `SyncControls` and `ResumeIcon`), to the `Sync` folder. * Update the related UI snapshot. * Add `SyncControls` to exports. * Move custom actions to the start (left) of `SyncControls` buttons. * Re-arrange icons in `IconType` alphabetically. --- ui/components/AutomationDetail.tsx | 2 +- ui/components/CheckboxActions.tsx | 119 - ui/components/DataTable/DataTable.tsx | 2 +- ui/components/Icon.tsx | 309 +- .../ImageAutomationDetails.tsx | 4 +- ui/components/SourceDetail.tsx | 4 +- ui/components/Sync/CheckboxActions.tsx | 94 + ui/components/Sync/ResumeIcon.tsx | 16 + ui/components/Sync/SyncActions.tsx | 86 + ui/components/Sync/SyncControls.tsx | 233 ++ .../__tests__/CheckboxActions.test.tsx | 2 +- .../{ => Sync}/__tests__/SyncActions.test.tsx | 8 +- .../Sync/__tests__/SyncControls.test.tsx | 105 + .../__snapshots__/SyncActions.test.tsx.snap | 1530 ++++++++++ .../__snapshots__/SyncControls.test.tsx.snap | 2473 +++++++++++++++++ ui/components/SyncActions.tsx | 79 - ui/components/SyncButton.tsx | 109 - .../__snapshots__/SyncActions.test.tsx.snap | 803 ------ ui/index.ts | 4 +- ui/lib/theme.ts | 28 + 20 files changed, 4741 insertions(+), 1269 deletions(-) delete mode 100644 ui/components/CheckboxActions.tsx create mode 100644 ui/components/Sync/CheckboxActions.tsx create mode 100644 ui/components/Sync/ResumeIcon.tsx create mode 100644 ui/components/Sync/SyncActions.tsx create mode 100644 ui/components/Sync/SyncControls.tsx rename ui/components/{ => Sync}/__tests__/CheckboxActions.test.tsx (93%) rename ui/components/{ => Sync}/__tests__/SyncActions.test.tsx (88%) create mode 100644 ui/components/Sync/__tests__/SyncControls.test.tsx create mode 100644 ui/components/Sync/__tests__/__snapshots__/SyncActions.test.tsx.snap create mode 100644 ui/components/Sync/__tests__/__snapshots__/SyncControls.test.tsx.snap delete mode 100644 ui/components/SyncActions.tsx delete mode 100644 ui/components/SyncButton.tsx delete mode 100644 ui/components/__tests__/__snapshots__/SyncActions.test.tsx.snap diff --git a/ui/components/AutomationDetail.tsx b/ui/components/AutomationDetail.tsx index f1f9b48cbd..33f5503e44 100644 --- a/ui/components/AutomationDetail.tsx +++ b/ui/components/AutomationDetail.tsx @@ -21,7 +21,7 @@ import ReconciledObjectsTable from "./ReconciledObjectsTable"; import ReconciliationGraph from "./ReconciliationGraph"; import RequestStateHandler from "./RequestStateHandler"; import SubRouterTabs, { RouterTab } from "./SubRouterTabs"; -import SyncActions from "./SyncActions"; +import SyncActions from "./Sync/SyncActions"; import Text from "./Text"; import Timestamp from "./Timestamp"; import YamlView from "./YamlView"; diff --git a/ui/components/CheckboxActions.tsx b/ui/components/CheckboxActions.tsx deleted file mode 100644 index 55daf53598..0000000000 --- a/ui/components/CheckboxActions.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { Tooltip } from "@material-ui/core"; -import _ from "lodash"; -import * as React from "react"; -import { useLocation } from "react-router-dom"; -import styled from "styled-components"; -import { useSyncFluxObject } from "../hooks/automations"; -import { useToggleSuspend } from "../hooks/flux"; -import { ObjectRef } from "../lib/api/core/types.pb"; -import { V2Routes } from "../lib/types"; -import Button from "./Button"; -import Flex from "./Flex"; -import Icon, { IconType } from "./Icon"; -import SyncButton from "./SyncButton"; - -export const makeObjects = (checked: string[], rows: any[]): ObjectRef[] => { - const objects = []; - checked.forEach((uid) => { - const row = _.find(rows, (row) => { - return uid === row.uid; - }); - if (row) - return objects.push({ - kind: row.type, - name: row.name, - namespace: row.namespace, - clusterName: row.clusterName, - }); - }); - return objects; -}; - -const DefaultSync: React.FC<{ reqObjects: ObjectRef[] }> = ({ reqObjects }) => { - const defaultSync = useSyncFluxObject(reqObjects); - const location = useLocation(); - const noSource = { - [V2Routes.Sources]: true, - [V2Routes.ImageRepositories]: true, - [V2Routes.ImageUpdates]: true, - }; - return ( - defaultSync.mutateAsync(opts)} - hideDropdown={noSource[location.pathname]} - /> - ); -}; - -const DefaultSuspend: React.FC<{ - reqObjects: ObjectRef[]; - suspend: boolean; -}> = ({ reqObjects, suspend }) => { - function createDefaultSuspendHandler( - reqObjects: ObjectRef[], - suspend: boolean - ) { - const result = useToggleSuspend( - { - objects: reqObjects, - suspend: suspend, - }, - reqObjects[0]?.kind === "HelmRelease" || - reqObjects[0]?.kind === "Kustomization" - ? "automations" - : "sources" - ); - return () => result.mutateAsync(); - } - - return ( - -
- -
-
- ); -}; - -type Props = { - className?: string; - checked?: string[]; - rows?: any[]; -}; - -function CheckboxActions({ className, checked = [], rows = [] }: Props) { - const [reqObjects, setReqObjects] = React.useState([]); - - React.useEffect(() => { - if (checked.length > 0 && rows.length) - setReqObjects(makeObjects(checked, rows)); - else setReqObjects([]); - }, [checked, rows]); - - return ( - - - - - - ); -} - -export default styled(CheckboxActions).attrs({ - className: CheckboxActions.name, -})` - margin-right: 8px; -`; diff --git a/ui/components/DataTable/DataTable.tsx b/ui/components/DataTable/DataTable.tsx index 1a676cec58..bd4aa9ef64 100644 --- a/ui/components/DataTable/DataTable.tsx +++ b/ui/components/DataTable/DataTable.tsx @@ -6,7 +6,6 @@ import styled from "styled-components"; import { ThemeTypes } from "../../contexts/AppContext"; import { SearchedNamespaces } from "../../lib/types"; import { IconButton } from "../Button"; -import CheckboxActions from "../CheckboxActions"; import ChipGroup from "../ChipGroup"; import FilterDialog, { FilterConfig, @@ -16,6 +15,7 @@ import FilterDialog, { import Flex from "../Flex"; import Icon, { IconType } from "../Icon"; import SearchField from "../SearchField"; +import CheckboxActions from "../Sync/CheckboxActions"; import { filterRows, filterSelectionsToQueryString, diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index 498e3bce28..34a170d0b2 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -28,8 +28,10 @@ import Remove from "@material-ui/icons/Remove"; import RemoveCircleIcon from "@material-ui/icons/RemoveCircle"; import SaveAltIcon from "@material-ui/icons/SaveAlt"; import SearchIcon from "@material-ui/icons/Search"; +import SettingsIcon from "@material-ui/icons/Settings"; import SkipNextIcon from "@material-ui/icons/SkipNext"; import SkipPreviousIcon from "@material-ui/icons/SkipPrevious"; +import SyncIcon from "@material-ui/icons/Sync"; import VerifiedUser from "@material-ui/icons/VerifiedUser"; import WarningIcon from "@material-ui/icons/Warning"; import * as React from "react"; @@ -56,69 +58,73 @@ import SourcesIcon from "./NavIcons/SourcesIcon"; import TemplatesIcon from "./NavIcons/TemplatesIcon"; import TerraformIcon from "./NavIcons/TerraformIcon"; import WorkspacesIcon from "./NavIcons/WorkspacesIcon"; +import ResumeIcon from "./Sync/ResumeIcon"; import Text from "./Text"; export enum IconType { - CheckMark, Account, - ExternalTab, AddIcon, - ArrowUpwardIcon, - ArrowDropDownIcon, + ApplicationsIcon, ArrowDownwardRoundedIcon, + ArrowDropDownIcon, + ArrowUpwardIcon, ArrowUpwardRoundedIcon, - KeyboardArrowRightIcon, - KeyboardArrowDownIcon, - DeleteIcon, - SaveAltIcon, - ErrorIcon, + CallMade, + CallReceived, CheckCircleIcon, - HourglassFullIcon, - NavigateNextIcon, - NavigateBeforeIcon, - SkipNextIcon, - SkipPreviousIcon, - RemoveCircleIcon, - FilterIcon, + CheckMark, ClearIcon, - SearchIcon, - LogoutIcon, - SuccessIcon, + ClustersIcon, + DeleteIcon, + DeliveryIcon, + DocsIcon, + EditIcon, + ErrorIcon, + ExploreIcon, + ExternalTab, FailedIcon, - SuspendedIcon, FileCopyIcon, - ReconcileIcon, + FilterIcon, + FindInPage, FluxIcon, FluxIconHover, - DocsIcon, - ApplicationsIcon, - PlayIcon, - PauseIcon, - NotificationsIcon, - SourcesIcon, - ImageAutomationIcon, - DeliveryIcon, GitOpsRunIcon, - PipelinesIcon, - TerraformIcon, GitOpsSetsIcon, + HourglassFullIcon, + ImageAutomationIcon, + InfoIcon, + KeyboardArrowDownIcon, + KeyboardArrowRightIcon, + LogoutIcon, + NavigateBeforeIcon, + NavigateNextIcon, + NotificationsIcon, + PauseIcon, + PendingActionIcon, + PipelinesIcon, + PlayIcon, PoliciesIcon, + Policy, PolicyConfigsIcon, - WorkspacesIcon, + ReconcileIcon, + Remove, + RemoveCircleIcon, + ResumeIcon, + SaveAltIcon, + SearchIcon, SecretsIcon, + SettingsIcon, + SkipNextIcon, + SkipPreviousIcon, + SourcesIcon, + SuccessIcon, + SuspendedIcon, + SyncIcon, TemplatesIcon, - ClustersIcon, - ExploreIcon, - PendingActionIcon, - InfoIcon, - CallReceived, - CallMade, - Remove, - EditIcon, + TerraformIcon, VerifiedUser, - Policy, - FindInPage, WarningIcon, + WorkspacesIcon, } type Props = { @@ -132,181 +138,192 @@ type Props = { function getIcon(i: IconType) { switch (i) { - case IconType.CheckMark: - return CheckCircleIcon; - case IconType.Account: return PersonIcon; - case IconType.ExternalTab: - return LaunchIcon; - case IconType.AddIcon: return AddIcon; + case IconType.ApplicationsIcon: + return ApplicationsIcon; + + case IconType.ArrowDownwardRoundedIcon: + return ArrowDownwardRoundedIcon; + + case IconType.ArrowDropDownIcon: + return ArrowDropDownIcon; + case IconType.ArrowUpwardIcon: return ArrowUpwardIcon; - case IconType.KeyboardArrowRightIcon: - return KeyboardArrowRightIcon; - case IconType.KeyboardArrowDownIcon: - return KeyboardArrowDownIcon; - case IconType.DeleteIcon: - return DeleteIcon; + case IconType.ArrowUpwardRoundedIcon: + return ArrowUpwardRoundedIcon; - case IconType.SaveAltIcon: - return SaveAltIcon; + case IconType.CallMade: + return CallMade; + + case IconType.CallReceived: + return CallReceived; case IconType.CheckCircleIcon: return CheckCircleIcon; - case IconType.HourglassFullIcon: - return HourglassFullIcon; + case IconType.CheckMark: + return CheckCircleIcon; + + case IconType.ClearIcon: + return ClearIcon; + + case IconType.ClustersIcon: + return ClustersIcon; + + case IconType.DeleteIcon: + return DeleteIcon; + + case IconType.DeliveryIcon: + return DeliveryIcon; + + case IconType.DocsIcon: + return DocsIcon; + + case IconType.EditIcon: + return EditIcon; case IconType.ErrorIcon: return ErrorIcon; - case IconType.NavigateNextIcon: - return NavigateNextIcon; - - case IconType.NavigateBeforeIcon: - return NavigateBeforeIcon; + case IconType.ExploreIcon: + return ExploreIcon; - case IconType.SkipNextIcon: - return SkipNextIcon; + case IconType.ExternalTab: + return LaunchIcon; - case IconType.SkipPreviousIcon: - return SkipPreviousIcon; + case IconType.FailedIcon: + return ErrorIcon; - case IconType.RemoveCircleIcon: - return RemoveCircleIcon; + case IconType.FileCopyIcon: + return FileCopyIcon; case IconType.FilterIcon: return FilterIcon; - case IconType.ClearIcon: - return ClearIcon; + case IconType.FindInPage: + return FindInPage; - case IconType.SearchIcon: - return SearchIcon; + case IconType.FluxIcon: + return FluxIcon; - case IconType.LogoutIcon: - return LogoutIcon; + case IconType.GitOpsRunIcon: + return GitOpsRunIcon; - case IconType.SuccessIcon: - return () => ; + case IconType.GitOpsSetsIcon: + return GitOpsSetsIcon; - case IconType.FailedIcon: - return ErrorIcon; + case IconType.HourglassFullIcon: + return HourglassFullIcon; - case IconType.SuspendedIcon: - return () => ; + case IconType.ImageAutomationIcon: + return ImageAutomationIcon; - case IconType.ReconcileIcon: - return () => ; + case IconType.InfoIcon: + return InfoIcon; - case IconType.PendingActionIcon: - return () => ; + case IconType.KeyboardArrowDownIcon: + return KeyboardArrowDownIcon; - case IconType.ArrowDropDownIcon: - return ArrowDropDownIcon; + case IconType.KeyboardArrowRightIcon: + return KeyboardArrowRightIcon; - case IconType.ArrowDownwardRoundedIcon: - return ArrowDownwardRoundedIcon; + case IconType.LogoutIcon: + return LogoutIcon; - case IconType.ArrowUpwardRoundedIcon: - return ArrowUpwardRoundedIcon; + case IconType.NavigateBeforeIcon: + return NavigateBeforeIcon; - case IconType.FileCopyIcon: - return FileCopyIcon; + case IconType.NavigateNextIcon: + return NavigateNextIcon; - case IconType.PlayIcon: - return PlayIcon; + case IconType.NotificationsIcon: + return NotificationsIcon; case IconType.PauseIcon: return PauseIcon; - case IconType.SourcesIcon: - return SourcesIcon; - - case IconType.ImageAutomationIcon: - return ImageAutomationIcon; - - case IconType.DeliveryIcon: - return DeliveryIcon; - - case IconType.GitOpsRunIcon: - return GitOpsRunIcon; + case IconType.PendingActionIcon: + return () => ; case IconType.PipelinesIcon: return PipelinesIcon; - case IconType.TerraformIcon: - return TerraformIcon; + case IconType.PlayIcon: + return PlayIcon; - case IconType.ApplicationsIcon: - return ApplicationsIcon; + case IconType.PoliciesIcon: + return PoliciesIcon; - case IconType.DocsIcon: - return DocsIcon; + case IconType.Policy: + return Policy; - case IconType.FluxIcon: - return FluxIcon; + case IconType.PolicyConfigsIcon: + return PolicyConfigsIcon; - case IconType.GitOpsSetsIcon: - return GitOpsSetsIcon; + case IconType.ReconcileIcon: + return () => ; - case IconType.NotificationsIcon: - return NotificationsIcon; + case IconType.Remove: + return Remove; - case IconType.PoliciesIcon: - return () => ; + case IconType.RemoveCircleIcon: + return RemoveCircleIcon; - case IconType.PolicyConfigsIcon: - return PolicyConfigsIcon; + case IconType.ResumeIcon: + return ResumeIcon; - case IconType.VerifiedUser: - return VerifiedUser; + case IconType.SaveAltIcon: + return SaveAltIcon; - case IconType.Policy: - return () => ; + case IconType.SearchIcon: + return SearchIcon; case IconType.SecretsIcon: return SecretsIcon; - case IconType.TemplatesIcon: - return TemplatesIcon; + case IconType.SettingsIcon: + return SettingsIcon; - case IconType.WorkspacesIcon: - return WorkspacesIcon; + case IconType.SkipNextIcon: + return SkipNextIcon; - case IconType.ClustersIcon: - return ClustersIcon; + case IconType.SkipPreviousIcon: + return SkipPreviousIcon; - case IconType.ExploreIcon: - return ExploreIcon; + case IconType.SourcesIcon: + return SourcesIcon; - case IconType.InfoIcon: - return InfoIcon; + case IconType.SuccessIcon: + return () => ; - case IconType.CallReceived: - return CallReceived; + case IconType.SuspendedIcon: + return () => ; - case IconType.CallMade: - return CallMade; + case IconType.SyncIcon: + return SyncIcon; - case IconType.Remove: - return Remove; + case IconType.TemplatesIcon: + return TemplatesIcon; - case IconType.EditIcon: - return EditIcon; + case IconType.TerraformIcon: + return TerraformIcon; - case IconType.FindInPage: - return FindInPage; + case IconType.VerifiedUser: + return VerifiedUser; case IconType.WarningIcon: return WarningIcon; + case IconType.WorkspacesIcon: + return WorkspacesIcon; + default: break; } diff --git a/ui/components/ImageAutomation/ImageAutomationDetails.tsx b/ui/components/ImageAutomation/ImageAutomationDetails.tsx index fada8cbacc..96c2b4f0a1 100644 --- a/ui/components/ImageAutomation/ImageAutomationDetails.tsx +++ b/ui/components/ImageAutomation/ImageAutomationDetails.tsx @@ -7,7 +7,7 @@ import Flex from "../Flex"; import PageStatus from "../PageStatus"; import HeaderRows, { RowItem } from "../Policies/Utils/HeaderRows"; import SubRouterTabs, { RouterTab } from "../SubRouterTabs"; -import SyncActions from "../SyncActions"; +import SyncActions from "../Sync/SyncActions"; import YamlView from "../YamlView"; interface Props { className?: string; @@ -38,7 +38,7 @@ const ImageAutomationDetails = ({ clusterName={clusterName} kind={kind} suspended={suspended} - hideDropdown + hideSyncOptions /> )} diff --git a/ui/components/SourceDetail.tsx b/ui/components/SourceDetail.tsx index 605976f51f..ea27887044 100644 --- a/ui/components/SourceDetail.tsx +++ b/ui/components/SourceDetail.tsx @@ -14,7 +14,7 @@ import LoadingPage from "./LoadingPage"; import Metadata from "./Metadata"; import PageStatus from "./PageStatus"; import SubRouterTabs, { RouterTab } from "./SubRouterTabs"; -import SyncActions from "./SyncActions"; +import SyncActions from "./Sync/SyncActions"; import YamlView from "./YamlView"; //must specify OCIRepository type, artifactMetadata causes errors on the Source type @@ -86,7 +86,7 @@ function SourceDetail({ className, source, info, type, customActions }: Props) { clusterName={clusterName} kind={type} suspended={suspended} - hideDropdown + hideSyncOptions customActions={customActions} /> diff --git a/ui/components/Sync/CheckboxActions.tsx b/ui/components/Sync/CheckboxActions.tsx new file mode 100644 index 0000000000..d3afdc9bb4 --- /dev/null +++ b/ui/components/Sync/CheckboxActions.tsx @@ -0,0 +1,94 @@ +import _ from "lodash"; +import * as React from "react"; +import { useLocation } from "react-router-dom"; +import styled from "styled-components"; +import { useSyncFluxObject } from "../../hooks/automations"; +import { useToggleSuspend } from "../../hooks/flux"; +import { ObjectRef } from "../../lib/api/core/types.pb"; +import { V2Routes } from "../../lib/types"; +import SyncControls, { SyncType } from "./SyncControls"; + +export const makeObjects = (checked: string[], rows: any[]): ObjectRef[] => { + const objects = []; + checked.forEach((uid) => { + const row = _.find(rows, (row) => { + return uid === row.uid; + }); + if (row) + return objects.push({ + kind: row.type, + name: row.name, + namespace: row.namespace, + clusterName: row.clusterName, + }); + }); + return objects; +}; + +const noSource = { + [V2Routes.Sources]: true, + [V2Routes.ImageRepositories]: true, + [V2Routes.ImageUpdates]: true, +}; + +function createSuspendHandler(reqObjects: ObjectRef[], suspend: boolean) { + const result = useToggleSuspend( + { + objects: reqObjects, + suspend: suspend, + }, + reqObjects[0]?.kind === "HelmRelease" || + reqObjects[0]?.kind === "Kustomization" + ? "automations" + : "sources" + ); + return () => result.mutateAsync(); +} + +type Props = { + className?: string; + checked?: string[]; + rows?: any[]; +}; + +function CheckboxActions({ className, checked = [], rows = [] }: Props) { + const [reqObjects, setReqObjects] = React.useState([]); + const location = useLocation(); + + React.useEffect(() => { + if (checked.length > 0 && rows.length) + setReqObjects(makeObjects(checked, rows)); + else setReqObjects([]); + }, [checked, rows]); + + const sync = useSyncFluxObject(reqObjects); + + const syncHandler = (syncType: SyncType) => { + sync.mutateAsync({ withSource: syncType === SyncType.WithSource }); + }; + + const disableButtons = !reqObjects[0]; + + return ( + + ); +} + +export default styled(CheckboxActions).attrs({ + className: CheckboxActions.name, +})` + width: 50%; + min-width: fit-content; + margin-right: 8px; +`; diff --git a/ui/components/Sync/ResumeIcon.tsx b/ui/components/Sync/ResumeIcon.tsx new file mode 100644 index 0000000000..8934b59145 --- /dev/null +++ b/ui/components/Sync/ResumeIcon.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; + +function ResumeIcon() { + return ( + + + + ); +} + +export default ResumeIcon; diff --git a/ui/components/Sync/SyncActions.tsx b/ui/components/Sync/SyncActions.tsx new file mode 100644 index 0000000000..674888cec6 --- /dev/null +++ b/ui/components/Sync/SyncActions.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import styled from "styled-components"; +import { useSyncFluxObject } from "../../hooks/automations"; +import { useToggleSuspend } from "../../hooks/flux"; +import { Kind } from "../../lib/api/core/types.pb"; +import SyncControls, { SyncType } from "./SyncControls"; + +interface Props { + name?: string; + namespace?: string; + clusterName?: string; + kind?: Kind; + suspended?: boolean; + hideSyncOptions?: boolean; + customActions?: JSX.Element[]; + className?: string; +} + +const SyncActions = ({ + name, + namespace, + clusterName, + kind, + suspended, + hideSyncOptions, + customActions, + className, +}: Props) => { + const sync = useSyncFluxObject([ + { + name, + namespace, + clusterName, + kind: kind, + }, + ]); + + const syncHandler = (syncType: SyncType) => { + sync.mutateAsync({ withSource: syncType === SyncType.WithSource }); + }; + + const objects = [ + { + name, + namespace, + clusterName, + kind: kind, + }, + ]; + + const suspend = useToggleSuspend( + { + objects: objects, + suspend: true, + }, + "object" + ); + + const resume = useToggleSuspend( + { + objects: objects, + suspend: false, + }, + "object" + ); + + return ( + suspend.mutateAsync()} + onResumeClick={() => resume.mutateAsync()} + /> + ); +}; + +export default styled(SyncActions)` + width: 50%; + min-width: fit-content; +`; diff --git a/ui/components/Sync/SyncControls.tsx b/ui/components/Sync/SyncControls.tsx new file mode 100644 index 0000000000..26061053b2 --- /dev/null +++ b/ui/components/Sync/SyncControls.tsx @@ -0,0 +1,233 @@ +import { + FormControl, + FormControlLabel, + Radio, + RadioGroup, + Tooltip, +} from "@material-ui/core"; +import { alpha } from "@material-ui/core/styles/colorManipulator"; +import React from "react"; +import styled, { keyframes } from "styled-components"; +import { ThemeTypes } from "../../contexts/AppContext"; +import Button from "../Button"; +import CustomActions from "../CustomActions"; +import Flex from "../Flex"; +import Icon, { IconType } from "../Icon"; +import Spacer from "../Spacer"; + +interface Props { + className?: string; + syncLoading?: boolean; + syncDisabled?: boolean; + suspendDisabled?: boolean; + resumeDisabled?: boolean; + hideSyncOptions?: boolean; + hideSuspend?: boolean; + tooltipSuffix?: string; + customActions?: JSX.Element[]; + onSyncClick: (syncType: SyncType) => void; + onSuspendClick?: () => void; + onResumeClick?: () => void; +} + +export enum SyncType { + WithSource = "WithSource", + WithoutSource = "WithoutSource", +} + +const rotateAnimation = keyframes` + 0% { transform: rotate(0deg); } + 100% { transform: rotate(-360deg); } +`; + +const SourceLabel = styled(FormControlLabel)` + &.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; + } + + .MuiFormControlLabel-label { + color: ${(props) => + props.theme.mode === ThemeTypes.Dark + ? props.theme.colors.neutral30 + : props.theme.colors.neutral40}; + + &.Mui-disabled { + color: ${(props) => + props.theme.mode === ThemeTypes.Dark + ? props.theme.colors.primary30 + : props.theme.colors.neutral20}; + } + } + + .MuiTypography-root { + margin-left: ${(props) => props.theme.spacing.xs}; + } +`; + +export const IconButton = styled(Button)` + &.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; + + :disabled { + svg { + fill: ${(props) => + props.theme.mode === ThemeTypes.Dark + ? props.theme.colors.primary30 + : props.theme.colors.neutral20}; + } + } + + :hover { + background-color: ${(props) => + props.theme.mode === ThemeTypes.Dark + ? alpha(props.theme.colors.primary10, 0.2) + : alpha(props.theme.colors.primary, 0.1)}; + } + } + &.MuiButton-text { + padding: 0; + } +`; + +const SyncControls = ({ + className, + syncLoading, + syncDisabled, + suspendDisabled, + resumeDisabled, + hideSyncOptions, + hideSuspend, + tooltipSuffix = "", + customActions, + onSyncClick, + onSuspendClick, + onResumeClick, +}: Props) => { + const [syncType, setSyncType] = React.useState( + hideSyncOptions ? SyncType.WithoutSource : SyncType.WithSource + ); + + const handleSyncTypeChange = (value: SyncType) => { + setSyncType(value); + }; + + const disableSyncButtons = syncDisabled || syncLoading; + + return ( + + {customActions && ( + <> + + + + )} + + + {!hideSyncOptions && ( + <> + + { + handleSyncTypeChange(event.target.value as SyncType); + }} + > + } + label="with Source" + disabled={disableSyncButtons} + /> + + } + label="without Source" + disabled={disableSyncButtons} + /> + + + + + )} + +
+ onSyncClick(syncType)} + > + + +
+
+ {!hideSuspend && ( + <> + + +
+ + + +
+
+ + +
+ + + +
+
+ + )} +
+ ); +}; + +export default styled(SyncControls)` + .sync-icon-button { + text-transform: uppercase; + pointer-events: none; + } + + .rotate-icon { + color: ${(props) => props.theme.colors.primary10}; + + animation: 1s linear infinite ${rotateAnimation}; + } +`; diff --git a/ui/components/__tests__/CheckboxActions.test.tsx b/ui/components/Sync/__tests__/CheckboxActions.test.tsx similarity index 93% rename from ui/components/__tests__/CheckboxActions.test.tsx rename to ui/components/Sync/__tests__/CheckboxActions.test.tsx index d576c377cc..0d0acdba02 100644 --- a/ui/components/__tests__/CheckboxActions.test.tsx +++ b/ui/components/Sync/__tests__/CheckboxActions.test.tsx @@ -1,5 +1,5 @@ import "jest-styled-components"; -import { Kind } from "../../lib/api/core/types.pb"; +import { Kind } from "../../../lib/api/core/types.pb"; import { makeObjects } from "../CheckboxActions"; describe("CheckboxActions", () => { diff --git a/ui/components/__tests__/SyncActions.test.tsx b/ui/components/Sync/__tests__/SyncActions.test.tsx similarity index 88% rename from ui/components/__tests__/SyncActions.test.tsx rename to ui/components/Sync/__tests__/SyncActions.test.tsx index fd790e8c7d..a6fc56bf5a 100644 --- a/ui/components/__tests__/SyncActions.test.tsx +++ b/ui/components/Sync/__tests__/SyncActions.test.tsx @@ -1,12 +1,12 @@ import "jest-styled-components"; import React from "react"; import renderer from "react-test-renderer"; -import { CoreClientContext } from "../../contexts/CoreClientContext"; +import { CoreClientContext } from "../../../contexts/CoreClientContext"; import { createCoreMockClient, withContext, withTheme, -} from "../../lib/test-utils"; +} from "../../../lib/test-utils"; import SyncActions from "../SyncActions"; describe("SyncActions", () => { @@ -45,13 +45,13 @@ describe("SyncActions", () => { .toJSON(); expect(tree).toMatchSnapshot(); }); - it("hideDropdown", () => { + it("hideSyncOptions", () => { const tree = renderer .create( withTheme( withContext( - + , "/", {} diff --git a/ui/components/Sync/__tests__/SyncControls.test.tsx b/ui/components/Sync/__tests__/SyncControls.test.tsx new file mode 100644 index 0000000000..e3a4f6498c --- /dev/null +++ b/ui/components/Sync/__tests__/SyncControls.test.tsx @@ -0,0 +1,105 @@ +import "jest-styled-components"; +import React from "react"; +import renderer from "react-test-renderer"; +import { CoreClientContext } from "../../../contexts/CoreClientContext"; +import { + createCoreMockClient, + withContext, + withTheme, +} from "../../../lib/test-utils"; +import SyncControls from "../SyncControls"; + +describe("SyncControls", () => { + describe("snapshots", () => { + const mockContext = { api: createCoreMockClient({}), featureFlags: {} }; + + it("non-suspended", () => { + const tree = renderer + .create( + withTheme( + withContext( + + {}} /> + , + "/", + {} + ) + ) + ) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + it("allButtonsDisabled", () => { + const tree = renderer + .create( + withTheme( + withContext( + + {}} + /> + , + "/", + {} + ) + ) + ) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + it("hideSyncOptions", () => { + const tree = renderer + .create( + withTheme( + withContext( + + {}} /> + , + "/", + {} + ) + ) + ) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + it("hideSuspend", () => { + const tree = renderer + .create( + withTheme( + withContext( + + {}} /> + , + "/", + {} + ) + ) + ) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + it("hasTooltipSuffix", () => { + const tree = renderer + .create( + withTheme( + withContext( + + {}} + /> + , + "/", + {} + ) + ) + ) + .toJSON(); + expect(tree).toMatchSnapshot(); + }); + }); +}); diff --git a/ui/components/Sync/__tests__/__snapshots__/SyncActions.test.tsx.snap b/ui/components/Sync/__tests__/__snapshots__/SyncActions.test.tsx.snap new file mode 100644 index 0000000000..1266319ecd --- /dev/null +++ b/ui/components/Sync/__tests__/__snapshots__/SyncActions.test.tsx.snap @@ -0,0 +1,1530 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SyncActions snapshots hideSyncOptions 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c5 svg { + height: 24px; + width: 24px; +} + +.c5 svg path.path-fill, +.c5 svg line.path-fill, +.c5 svg polygon.path-fill, +.c5 svg rect.path-fill, +.c5 svg circle.path-fill, +.c5 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg path.stroke-fill, +.c5 svg line.stroke-fill, +.c5 svg polygon.stroke-fill, +.c5 svg rect.stroke-fill, +.c5 svg circle.stroke-fill, +.c5 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c5.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c5.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c5 img { + width: 24px; +} + +.c8 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c8 svg path.path-fill, +.c8 svg line.path-fill, +.c8 svg polygon.path-fill, +.c8 svg rect.path-fill, +.c8 svg circle.path-fill, +.c8 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg path.stroke-fill, +.c8 svg line.stroke-fill, +.c8 svg polygon.stroke-fill, +.c8 svg rect.stroke-fill, +.c8 svg circle.stroke-fill, +.c8 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c8.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c8.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c8 img { + width: 24px; +} + +.c6 { + padding: 4px; +} + +.c3.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c3.MuiButton-outlined { + padding: 8px 12px; +} + +.c7.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c7.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c7.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c7.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +.c2 { + width: 50%; + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; +} + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+`; + +exports[`SyncActions snapshots non-suspended 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c5 svg { + height: 24px; + width: 24px; +} + +.c5 svg path.path-fill, +.c5 svg line.path-fill, +.c5 svg polygon.path-fill, +.c5 svg rect.path-fill, +.c5 svg circle.path-fill, +.c5 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg path.stroke-fill, +.c5 svg line.stroke-fill, +.c5 svg polygon.stroke-fill, +.c5 svg rect.stroke-fill, +.c5 svg circle.stroke-fill, +.c5 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c5.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c5.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c5 img { + width: 24px; +} + +.c9 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c9 svg path.path-fill, +.c9 svg line.path-fill, +.c9 svg polygon.path-fill, +.c9 svg rect.path-fill, +.c9 svg circle.path-fill, +.c9 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c9 svg path.stroke-fill, +.c9 svg line.stroke-fill, +.c9 svg polygon.stroke-fill, +.c9 svg rect.stroke-fill, +.c9 svg circle.stroke-fill, +.c9 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c9 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c9.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c9.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c9 img { + width: 24px; +} + +.c6 { + padding: 4px; +} + +.c3.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c3.MuiButton-outlined { + padding: 8px 12px; +} + +.c7.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c7 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c7 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c7 .MuiTypography-root { + margin-left: 8px; +} + +.c8.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c8.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c8.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c8.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +.c2 { + width: 50%; + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+`; + +exports[`SyncActions snapshots suspended 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c5 svg { + height: 24px; + width: 24px; +} + +.c5 svg path.path-fill, +.c5 svg line.path-fill, +.c5 svg polygon.path-fill, +.c5 svg rect.path-fill, +.c5 svg circle.path-fill, +.c5 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg path.stroke-fill, +.c5 svg line.stroke-fill, +.c5 svg polygon.stroke-fill, +.c5 svg rect.stroke-fill, +.c5 svg circle.stroke-fill, +.c5 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c5 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c5.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c5.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c5 img { + width: 24px; +} + +.c9 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c9 svg path.path-fill, +.c9 svg line.path-fill, +.c9 svg polygon.path-fill, +.c9 svg rect.path-fill, +.c9 svg circle.path-fill, +.c9 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c9 svg path.stroke-fill, +.c9 svg line.stroke-fill, +.c9 svg polygon.stroke-fill, +.c9 svg rect.stroke-fill, +.c9 svg circle.stroke-fill, +.c9 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c9 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c9.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c9.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c9 img { + width: 24px; +} + +.c6 { + padding: 4px; +} + +.c3.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c3.MuiButton-outlined { + padding: 8px 12px; +} + +.c7.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c7 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c7 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c7 .MuiTypography-root { + margin-left: 8px; +} + +.c8.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c8.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c8.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c8.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +.c2 { + width: 50%; + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+`; diff --git a/ui/components/Sync/__tests__/__snapshots__/SyncControls.test.tsx.snap b/ui/components/Sync/__tests__/__snapshots__/SyncControls.test.tsx.snap new file mode 100644 index 0000000000..7d728a962b --- /dev/null +++ b/ui/components/Sync/__tests__/__snapshots__/SyncControls.test.tsx.snap @@ -0,0 +1,2473 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SyncControls snapshots allButtonsDisabled 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 svg { + height: 24px; + width: 24px; +} + +.c4 svg path.path-fill, +.c4 svg line.path-fill, +.c4 svg polygon.path-fill, +.c4 svg rect.path-fill, +.c4 svg circle.path-fill, +.c4 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg path.stroke-fill, +.c4 svg line.stroke-fill, +.c4 svg polygon.stroke-fill, +.c4 svg rect.stroke-fill, +.c4 svg circle.stroke-fill, +.c4 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c4.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c4.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c4 img { + width: 24px; +} + +.c8 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c8 svg path.path-fill, +.c8 svg line.path-fill, +.c8 svg polygon.path-fill, +.c8 svg rect.path-fill, +.c8 svg circle.path-fill, +.c8 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg path.stroke-fill, +.c8 svg line.stroke-fill, +.c8 svg polygon.stroke-fill, +.c8 svg rect.stroke-fill, +.c8 svg circle.stroke-fill, +.c8 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c8.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c8.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c8 img { + width: 24px; +} + +.c5 { + padding: 4px; +} + +.c2.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c2.MuiButton-outlined { + padding: 8px 12px; +} + +.c6.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c6 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c6 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c6 .MuiTypography-root { + margin-left: 8px; +} + +.c7.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c7.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c7.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c7.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+`; + +exports[`SyncControls snapshots hasTooltipSuffix 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 svg { + height: 24px; + width: 24px; +} + +.c4 svg path.path-fill, +.c4 svg line.path-fill, +.c4 svg polygon.path-fill, +.c4 svg rect.path-fill, +.c4 svg circle.path-fill, +.c4 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg path.stroke-fill, +.c4 svg line.stroke-fill, +.c4 svg polygon.stroke-fill, +.c4 svg rect.stroke-fill, +.c4 svg circle.stroke-fill, +.c4 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c4.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c4.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c4 img { + width: 24px; +} + +.c8 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c8 svg path.path-fill, +.c8 svg line.path-fill, +.c8 svg polygon.path-fill, +.c8 svg rect.path-fill, +.c8 svg circle.path-fill, +.c8 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg path.stroke-fill, +.c8 svg line.stroke-fill, +.c8 svg polygon.stroke-fill, +.c8 svg rect.stroke-fill, +.c8 svg circle.stroke-fill, +.c8 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c8.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c8.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c8 img { + width: 24px; +} + +.c5 { + padding: 4px; +} + +.c2.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c2.MuiButton-outlined { + padding: 8px 12px; +} + +.c6.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c6 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c6 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c6 .MuiTypography-root { + margin-left: 8px; +} + +.c7.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c7.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c7.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c7.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+`; + +exports[`SyncControls snapshots hideSuspend 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 svg { + height: 24px; + width: 24px; +} + +.c4 svg path.path-fill, +.c4 svg line.path-fill, +.c4 svg polygon.path-fill, +.c4 svg rect.path-fill, +.c4 svg circle.path-fill, +.c4 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg path.stroke-fill, +.c4 svg line.stroke-fill, +.c4 svg polygon.stroke-fill, +.c4 svg rect.stroke-fill, +.c4 svg circle.stroke-fill, +.c4 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c4.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c4.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c4 img { + width: 24px; +} + +.c5 { + padding: 4px; +} + +.c2.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c2.MuiButton-outlined { + padding: 8px 12px; +} + +.c6.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c6 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c6 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c6 .MuiTypography-root { + margin-left: 8px; +} + +.c7.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c7.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c7.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c7.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+`; + +exports[`SyncControls snapshots hideSyncOptions 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 svg { + height: 24px; + width: 24px; +} + +.c4 svg path.path-fill, +.c4 svg line.path-fill, +.c4 svg polygon.path-fill, +.c4 svg rect.path-fill, +.c4 svg circle.path-fill, +.c4 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg path.stroke-fill, +.c4 svg line.stroke-fill, +.c4 svg polygon.stroke-fill, +.c4 svg rect.stroke-fill, +.c4 svg circle.stroke-fill, +.c4 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c4.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c4.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c4 img { + width: 24px; +} + +.c7 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c7 svg path.path-fill, +.c7 svg line.path-fill, +.c7 svg polygon.path-fill, +.c7 svg rect.path-fill, +.c7 svg circle.path-fill, +.c7 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c7 svg path.stroke-fill, +.c7 svg line.stroke-fill, +.c7 svg polygon.stroke-fill, +.c7 svg rect.stroke-fill, +.c7 svg circle.stroke-fill, +.c7 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c7 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c7.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c7.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c7 img { + width: 24px; +} + +.c5 { + padding: 4px; +} + +.c2.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c2.MuiButton-outlined { + padding: 8px 12px; +} + +.c6.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c6.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c6.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c6.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+`; + +exports[`SyncControls snapshots non-suspended 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 svg { + height: 24px; + width: 24px; +} + +.c4 svg path.path-fill, +.c4 svg line.path-fill, +.c4 svg polygon.path-fill, +.c4 svg rect.path-fill, +.c4 svg circle.path-fill, +.c4 svg polyline.path-fill { + fill: !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg path.stroke-fill, +.c4 svg line.stroke-fill, +.c4 svg polygon.stroke-fill, +.c4 svg rect.stroke-fill, +.c4 svg circle.stroke-fill, +.c4 svg polyline.stroke-fill { + stroke: !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c4 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c4.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c4.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c4 img { + width: 24px; +} + +.c8 svg { + fill: #009CCC; + height: 24px; + width: 24px; +} + +.c8 svg path.path-fill, +.c8 svg line.path-fill, +.c8 svg polygon.path-fill, +.c8 svg rect.path-fill, +.c8 svg circle.path-fill, +.c8 svg polyline.path-fill { + fill: #009CCC !important; + -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg path.stroke-fill, +.c8 svg line.stroke-fill, +.c8 svg polygon.stroke-fill, +.c8 svg rect.stroke-fill, +.c8 svg circle.stroke-fill, +.c8 svg polyline.stroke-fill { + stroke: #009CCC !important; + -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; + transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; +} + +.c8 svg rect.rect-height { + height: 24px; + width: 24px; +} + +.c8.downward { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.c8.upward { + -webkit-transform: initial; + -ms-transform: initial; + transform: initial; +} + +.c8 img { + width: 24px; +} + +.c5 { + padding: 4px; +} + +.c2.MuiButton-root { + height: 32px; + font-size: 12px; + -webkit-letter-spacing: 1px; + -moz-letter-spacing: 1px; + -ms-letter-spacing: 1px; + letter-spacing: 1px; + line-height: 1; + border-radius: 2px; + font-weight: 600; +} + +.c2.MuiButton-outlined { + padding: 8px 12px; +} + +.c6.MuiFormControlLabel-root { + margin-right: 0; + margin-left: 0; +} + +.c6 .MuiFormControlLabel-label { + color: #1a1a1a; +} + +.c6 .MuiFormControlLabel-label.Mui-disabled { + color: #d8d8d8; +} + +.c6 .MuiTypography-root { + margin-left: 8px; +} + +.c7.MuiButton-root { + border-radius: 50%; + min-width: 32px; + height: 32px; + padding: 0; +} + +.c7.MuiButton-root:disabled svg { + fill: #d8d8d8; +} + +.c7.MuiButton-root:hover { + background-color: rgba(0,179,236,0.1); +} + +.c7.MuiButton-text { + padding: 0; +} + +.c1 .sync-icon-button { + text-transform: uppercase; + pointer-events: none; +} + +.c1 .rotate-icon { + color: #009CCC; + -webkit-animation: 1s linear infinite dxlcOg; + animation: 1s linear infinite dxlcOg; +} + +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+`; diff --git a/ui/components/SyncActions.tsx b/ui/components/SyncActions.tsx deleted file mode 100644 index c883c9938d..0000000000 --- a/ui/components/SyncActions.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import { useSyncFluxObject } from "../hooks/automations"; -import { useToggleSuspend } from "../hooks/flux"; -import { Kind } from "../lib/api/core/types.pb"; -import Button from "./Button"; -import CustomActions from "./CustomActions"; -import Flex from "./Flex"; -import SyncButton from "./SyncButton"; - -interface Props { - name?: string; - namespace?: string; - clusterName?: string; - kind?: Kind; - suspended?: boolean; - hideDropdown?: boolean; - customActions?: JSX.Element[]; - className?: string; -} - -const SyncActions = ({ - name, - namespace, - clusterName, - kind, - suspended, - hideDropdown, - customActions, - className, -}: Props) => { - const suspend = useToggleSuspend( - { - objects: [ - { - name, - namespace, - clusterName, - kind: kind, - }, - ], - suspend: !suspended, - }, - "object" - ); - - const sync = useSyncFluxObject([ - { - name, - namespace, - clusterName, - kind: kind, - }, - ]); - - const syncHandler = hideDropdown - ? () => sync.mutateAsync({ withSource: false }) - : (opts) => sync.mutateAsync(opts); - - return ( - - - - - - ); -}; - -export default styled(SyncActions)` - width: 50%; - min-width: fit-content; -`; diff --git a/ui/components/SyncButton.tsx b/ui/components/SyncButton.tsx deleted file mode 100644 index 3ae7dec5a4..0000000000 --- a/ui/components/SyncButton.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from "react"; -import styled from "styled-components"; -import Button, { IconButton } from "./Button"; -import Flex from "./Flex"; -import Icon, { IconType } from "./Icon"; - -type Props = { - className?: string; - loading?: boolean; - disabled?: boolean; - onClick: (opts: { withSource: boolean }) => void; - hideDropdown?: boolean; -}; - -export const ArrowDropDown = styled(IconButton)` - &.MuiButton-outlined { - //2px = MUI radius - border-radius: 0 2px 2px 0; - } - &.MuiButton-outlinedPrimary { - border-color: ${(props) => props.theme.colors.neutral20}; - } - &.MuiButton-root { - min-width: 0; - height: 32px; - padding: 8px 4px; - } - &.MuiButton-text { - padding: 0; - } -`; - -const Sync = styled(Button)<{ $hideDropdown: boolean }>` - &.MuiButton-outlined { - margin-right: 0; - ${(props) => - !props.$hideDropdown && - `border-radius: 2px 0 0 2px; border-right: none; &.MuiButton-outlined.Mui-disabled { - border-right: none };`} - } -`; - -export const DropDown = styled(Flex)` - position: absolute; - overflow: hidden; - background: ${(props) => props.theme.colors.neutral00}; - height: ${(props) => (props.open ? "100%" : "0px")}; - transition-property: height; - transition-duration: 0.2s; - transition-timing-function: ease-in-out; - z-index: 1; -`; - -function SyncButton({ - className, - loading, - disabled, - onClick, - hideDropdown = false, -}: Props) { - const [open, setOpen] = React.useState(false); - let arrowDropDown; - if (hideDropdown == false) { - arrowDropDown = ( - setOpen(!open)} - disabled={disabled} - > - - - ); - } else { - arrowDropDown = <>; - } - return ( -
- - onClick({ withSource: true })} - //$ - transient prop that is not passed to DOM https://styled-components.com/docs/api#transient-props - $hideDropdown={hideDropdown} - > - Sync - - {arrowDropDown} - - - - -
- ); -} - -export default styled(SyncButton).attrs({ className: SyncButton.name })``; diff --git a/ui/components/__tests__/__snapshots__/SyncActions.test.tsx.snap b/ui/components/__tests__/__snapshots__/SyncActions.test.tsx.snap deleted file mode 100644 index 670a346298..0000000000 --- a/ui/components/__tests__/__snapshots__/SyncActions.test.tsx.snap +++ /dev/null @@ -1,803 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SyncActions snapshots hideDropdown 1`] = ` -.c0 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; - gap: 12px; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; -} - -.c3.MuiButton-root { - height: 32px; - font-size: 12px; - -webkit-letter-spacing: 1px; - -moz-letter-spacing: 1px; - -ms-letter-spacing: 1px; - letter-spacing: 1px; - line-height: 1; - border-radius: 2px; - font-weight: 600; -} - -.c3.MuiButton-outlined { - padding: 8px 12px; -} - -.c4.MuiButton-outlined { - margin-right: 0; -} - -.c5 { - position: absolute; - overflow: hidden; - background: #ffffff; - height: 0px; - -webkit-transition-property: height; - transition-property: height; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - z-index: 1; -} - -.c1 { - width: 50%; - min-width: -webkit-fit-content; - min-width: -moz-fit-content; - min-width: fit-content; -} - -
-
-
- -
-
- -
-
- -
-`; - -exports[`SyncActions snapshots non-suspended 1`] = ` -.c0 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; - gap: 12px; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; -} - -.c7 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c8 svg { - height: 16px; - width: 16px; -} - -.c8 svg path.path-fill, -.c8 svg line.path-fill, -.c8 svg polygon.path-fill, -.c8 svg rect.path-fill, -.c8 svg circle.path-fill, -.c8 svg polyline.path-fill { - fill: !important; - -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; - transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; -} - -.c8 svg path.stroke-fill, -.c8 svg line.stroke-fill, -.c8 svg polygon.stroke-fill, -.c8 svg rect.stroke-fill, -.c8 svg circle.stroke-fill, -.c8 svg polyline.stroke-fill { - stroke: !important; - -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; - transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; -} - -.c8 svg rect.rect-height { - height: 16px; - width: 16px; -} - -.c8.downward { - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} - -.c8.upward { - -webkit-transform: initial; - -ms-transform: initial; - transform: initial; -} - -.c8 img { - width: 16px; -} - -.c3.MuiButton-root { - height: 32px; - font-size: 12px; - -webkit-letter-spacing: 1px; - -moz-letter-spacing: 1px; - -ms-letter-spacing: 1px; - letter-spacing: 1px; - line-height: 1; - border-radius: 2px; - font-weight: 600; -} - -.c3.MuiButton-outlined { - padding: 8px 12px; -} - -.c5.MuiButton-root { - border-radius: 50%; - min-width: 38px; - height: 38px; - padding: 0; -} - -.c5.MuiButton-text { - padding: 0; -} - -.c6.MuiButton-outlined { - border-radius: 0 2px 2px 0; -} - -.c6.MuiButton-outlinedPrimary { - border-color: #d8d8d8; -} - -.c6.MuiButton-root { - min-width: 0; - height: 32px; - padding: 8px 4px; -} - -.c6.MuiButton-text { - padding: 0; -} - -.c4.MuiButton-outlined { - margin-right: 0; - border-radius: 2px 0 0 2px; - border-right: none; -} - -.c4.MuiButton-outlined.MuiButton-outlined.Mui-disabled { - border-right: none; -} - -.c9 { - position: absolute; - overflow: hidden; - background: #ffffff; - height: 0px; - -webkit-transition-property: height; - transition-property: height; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - z-index: 1; -} - -.c1 { - width: 50%; - min-width: -webkit-fit-content; - min-width: -moz-fit-content; - min-width: fit-content; -} - -
-
-
- - -
-
- -
-
- -
-`; - -exports[`SyncActions snapshots suspended 1`] = ` -.c0 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; - gap: 12px; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: start; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: start; -} - -.c7 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c8 svg { - height: 16px; - width: 16px; -} - -.c8 svg path.path-fill, -.c8 svg line.path-fill, -.c8 svg polygon.path-fill, -.c8 svg rect.path-fill, -.c8 svg circle.path-fill, -.c8 svg polyline.path-fill { - fill: !important; - -webkit-transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; - transition: fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms; -} - -.c8 svg path.stroke-fill, -.c8 svg line.stroke-fill, -.c8 svg polygon.stroke-fill, -.c8 svg rect.stroke-fill, -.c8 svg circle.stroke-fill, -.c8 svg polyline.stroke-fill { - stroke: !important; - -webkit-transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; - transition: stroke 200ms cubic-bezier(0.4,0,0.2,1) 0ms; -} - -.c8 svg rect.rect-height { - height: 16px; - width: 16px; -} - -.c8.downward { - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} - -.c8.upward { - -webkit-transform: initial; - -ms-transform: initial; - transform: initial; -} - -.c8 img { - width: 16px; -} - -.c3.MuiButton-root { - height: 32px; - font-size: 12px; - -webkit-letter-spacing: 1px; - -moz-letter-spacing: 1px; - -ms-letter-spacing: 1px; - letter-spacing: 1px; - line-height: 1; - border-radius: 2px; - font-weight: 600; -} - -.c3.MuiButton-outlined { - padding: 8px 12px; -} - -.c5.MuiButton-root { - border-radius: 50%; - min-width: 38px; - height: 38px; - padding: 0; -} - -.c5.MuiButton-text { - padding: 0; -} - -.c6.MuiButton-outlined { - border-radius: 0 2px 2px 0; -} - -.c6.MuiButton-outlinedPrimary { - border-color: #d8d8d8; -} - -.c6.MuiButton-root { - min-width: 0; - height: 32px; - padding: 8px 4px; -} - -.c6.MuiButton-text { - padding: 0; -} - -.c4.MuiButton-outlined { - margin-right: 0; - border-radius: 2px 0 0 2px; - border-right: none; -} - -.c4.MuiButton-outlined.MuiButton-outlined.Mui-disabled { - border-right: none; -} - -.c9 { - position: absolute; - overflow: hidden; - background: #ffffff; - height: 0px; - -webkit-transition-property: height; - transition-property: height; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - z-index: 1; -} - -.c1 { - width: 50%; - min-width: -webkit-fit-content; - min-width: -moz-fit-content; - min-width: fit-content; -} - -
-
-
- - -
-
- -
-
- -
-`; diff --git a/ui/index.ts b/ui/index.ts index b3a45b8aff..236838c2a7 100644 --- a/ui/index.ts +++ b/ui/index.ts @@ -70,7 +70,7 @@ import SourceLink from "./components/SourceLink"; import SourcesTable from "./components/SourcesTable"; import Spacer from "./components/Spacer"; import SubRouterTabs, { RouterTab } from "./components/SubRouterTabs"; -import SyncButton from "./components/SyncButton"; +import SyncControls from "./components/Sync/SyncControls"; import Text from "./components/Text"; import Timestamp from "./components/Timestamp"; import UserGroupsTable from "./components/UserGroupsTable"; @@ -240,7 +240,7 @@ export { SourcesTable, Spacer, SubRouterTabs, - SyncButton, + SyncControls, Text, ThemeTypes, Timestamp, diff --git a/ui/lib/theme.ts b/ui/lib/theme.ts index b0f659d87d..6ec98571a5 100644 --- a/ui/lib/theme.ts +++ b/ui/lib/theme.ts @@ -1,5 +1,6 @@ // Typescript will handle type-checking/linting for this file import { createTheme } from "@material-ui/core"; +import { alpha } from "@material-ui/core/styles/colorManipulator"; // eslint-disable-next-line import { createGlobalStyle, DefaultTheme } from "styled-components"; import { ThemeTypes } from "../contexts/AppContext"; @@ -274,5 +275,32 @@ export const muiTheme = (colors, mode) => }, }, }, + // radio buttons + MuiRadio: { + root: { + padding: 0, + color: colors.primary30, + }, + + colorSecondary: { + color: colors.primary30, + + "&:hover": { + backgroundColor: ThemeTypes.Dark + ? alpha(colors.primary10, 0.2) + : alpha(colors.primary, 0.1), + color: colors.primary10, + }, + + "&$checked": { + color: colors.primary10, + }, + + "&$disabled": { + color: + mode === ThemeTypes.Dark ? colors.primary30 : colors.neutral20, + }, + }, + }, }, }); From a659b94e55cffe2b878be4cf6e732e27f520df9d Mon Sep 17 00:00:00 2001 From: ahussein3 Date: Wed, 1 Nov 2023 13:50:34 +0200 Subject: [PATCH 12/23] add new svg icon as CLusterDiscovery icon --- ui/components/Icon.tsx | 5 +++++ ui/components/NavIcons/ClusterDiscoveryIcon.tsx | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 ui/components/NavIcons/ClusterDiscoveryIcon.tsx diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index 34a170d0b2..0e89560c3f 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -58,6 +58,7 @@ import SourcesIcon from "./NavIcons/SourcesIcon"; import TemplatesIcon from "./NavIcons/TemplatesIcon"; import TerraformIcon from "./NavIcons/TerraformIcon"; import WorkspacesIcon from "./NavIcons/WorkspacesIcon"; +import ClusaterDiscoveryIcon from "./NavIcons/ClusterDiscoveryIcon"; import ResumeIcon from "./Sync/ResumeIcon"; import Text from "./Text"; @@ -125,6 +126,7 @@ export enum IconType { VerifiedUser, WarningIcon, WorkspacesIcon, + ClusterDiscoveryIcon, } type Props = { @@ -324,6 +326,9 @@ function getIcon(i: IconType) { case IconType.WorkspacesIcon: return WorkspacesIcon; + case IconType.ClusterDiscoveryIcon: + return ClusaterDiscoveryIcon; + default: break; } diff --git a/ui/components/NavIcons/ClusterDiscoveryIcon.tsx b/ui/components/NavIcons/ClusterDiscoveryIcon.tsx new file mode 100644 index 0000000000..52abcc2fef --- /dev/null +++ b/ui/components/NavIcons/ClusterDiscoveryIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; + +function ClustersIcon() { + return ( + + + + ); +} + +export default ClustersIcon; From 7ea281f484b79c198affe4fe6a137d95c7ea30ab Mon Sep 17 00:00:00 2001 From: ahussein3 Date: Wed, 1 Nov 2023 13:52:27 +0200 Subject: [PATCH 13/23] fix typo --- ui/components/Icon.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index 0e89560c3f..fdfa0609f4 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -58,7 +58,7 @@ import SourcesIcon from "./NavIcons/SourcesIcon"; import TemplatesIcon from "./NavIcons/TemplatesIcon"; import TerraformIcon from "./NavIcons/TerraformIcon"; import WorkspacesIcon from "./NavIcons/WorkspacesIcon"; -import ClusaterDiscoveryIcon from "./NavIcons/ClusterDiscoveryIcon"; +import ClusterDiscoveryIcon from "./NavIcons/ClusterDiscoveryIcon"; import ResumeIcon from "./Sync/ResumeIcon"; import Text from "./Text"; @@ -327,7 +327,7 @@ function getIcon(i: IconType) { return WorkspacesIcon; case IconType.ClusterDiscoveryIcon: - return ClusaterDiscoveryIcon; + return ClusterDiscoveryIcon; default: break; From 98d8153d9c99e4bbfc6e508aa1b533ca74fbfac5 Mon Sep 17 00:00:00 2001 From: ahussein3 Date: Wed, 1 Nov 2023 14:16:02 +0200 Subject: [PATCH 14/23] update import order --- ui/components/Icon.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index fdfa0609f4..dced593c30 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -41,6 +41,7 @@ import images from "../lib/images"; import { colors, fontSizes, spacing } from "../typedefs/styled"; import Flex from "./Flex"; import ApplicationsIcon from "./NavIcons/ApplicationsIcon"; +import ClusterDiscoveryIcon from "./NavIcons/ClusterDiscoveryIcon"; import ClustersIcon from "./NavIcons/ClustersIcon"; import DeliveryIcon from "./NavIcons/DeliveryIcon"; import DocsIcon from "./NavIcons/DocsIcon"; @@ -58,7 +59,7 @@ import SourcesIcon from "./NavIcons/SourcesIcon"; import TemplatesIcon from "./NavIcons/TemplatesIcon"; import TerraformIcon from "./NavIcons/TerraformIcon"; import WorkspacesIcon from "./NavIcons/WorkspacesIcon"; -import ClusterDiscoveryIcon from "./NavIcons/ClusterDiscoveryIcon"; + import ResumeIcon from "./Sync/ResumeIcon"; import Text from "./Text"; @@ -75,6 +76,7 @@ export enum IconType { CheckCircleIcon, CheckMark, ClearIcon, + ClusterDiscoveryIcon, ClustersIcon, DeleteIcon, DeliveryIcon, @@ -126,7 +128,6 @@ export enum IconType { VerifiedUser, WarningIcon, WorkspacesIcon, - ClusterDiscoveryIcon, } type Props = { @@ -176,6 +177,9 @@ function getIcon(i: IconType) { case IconType.ClearIcon: return ClearIcon; + case IconType.ClusterDiscoveryIcon: + return ClusterDiscoveryIcon; + case IconType.ClustersIcon: return ClustersIcon; @@ -326,9 +330,6 @@ function getIcon(i: IconType) { case IconType.WorkspacesIcon: return WorkspacesIcon; - case IconType.ClusterDiscoveryIcon: - return ClusterDiscoveryIcon; - default: break; } From 572cde9121ad8ac4a41b39f30012448ae6526056 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:21:09 +0000 Subject: [PATCH 15/23] build(deps): Bump google.golang.org/grpc from 1.51.0 to 1.56.3 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.51.0 to 1.56.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.51.0...v1.56.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 7790fa7a11..1ff62f96d7 100644 --- a/go.mod +++ b/go.mod @@ -47,13 +47,13 @@ require ( github.com/spf13/viper v1.13.0 github.com/tomwright/dasel v1.22.1 github.com/weaveworks/policy-agent/api v1.0.5 - github.com/weaveworks/tf-controller/tfctl v0.0.0-20230523160934-c6613bfc7fff + github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7 github.com/yannh/kubeconform v0.5.0 go.uber.org/zap v1.25.0 golang.org/x/crypto v0.13.0 golang.org/x/oauth2 v0.7.0 - google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 - google.golang.org/grpc v1.51.0 + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 + google.golang.org/grpc v1.56.3 google.golang.org/protobuf v1.30.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v3 v3.0.1 @@ -84,7 +84,7 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/glog v1.0.0 // indirect + github.com/golang/glog v1.1.0 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-github/v52 v52.0.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect diff --git a/go.sum b/go.sum index 388a06927f..582b2052dc 100644 --- a/go.sum +++ b/go.sum @@ -229,8 +229,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -579,8 +579,8 @@ github.com/weaveworks/policy-agent/api v1.0.5 h1:4pqzzta8xPUsE9h9YhGJVg5XQ2NuAU/ github.com/weaveworks/policy-agent/api v1.0.5/go.mod h1:LlhTiipsV5GSHkL/q7Wa7qrJPZGR9Xtoq2npXTWp9bI= github.com/weaveworks/tf-controller/api v0.0.0-20230416092146-4a7dfa5b6cc4 h1:+IkLtnXzCkhJzojbadPd+UxwaTa6K/Eb2grY6LcYfeo= github.com/weaveworks/tf-controller/api v0.0.0-20230416092146-4a7dfa5b6cc4/go.mod h1:LUBkwqS7FHz/QTNuYzvWj6svehhh1djnV0Gj3OTc87E= -github.com/weaveworks/tf-controller/tfctl v0.0.0-20230523160934-c6613bfc7fff h1:7W9BOtaeNX4vka2mZelcMCv8BMBmzVPAlBO9iUZQ4qM= -github.com/weaveworks/tf-controller/tfctl v0.0.0-20230523160934-c6613bfc7fff/go.mod h1:9RFuqlMWSGaFs6qBST17YltvzNIhcTb8A+VKJ5qJnLY= +github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7 h1:q5qCxFXkRG49L/zL9ArcujuJT+MtQa4swQzAZApwV78= +github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7/go.mod h1:4DPsiECdMYBva1U9xbRt8+gnyCCc2lRMbyiHrtUOkuo= github.com/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw= github.com/xanzy/go-gitlab v0.83.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -978,8 +978,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 h1:1aEQRgZ4Gks2SRAkLzIPpIszRazwVfjSFe1cKc+e0Jg= -google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1000,8 +1000,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 7ca3e8e560d14b31966fe97419e345979b041e84 Mon Sep 17 00:00:00 2001 From: yiannis Date: Wed, 1 Nov 2023 11:46:43 +0000 Subject: [PATCH 16/23] build(deps): Bump github.com/weaveworks/tf-controller/api version --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 1ff62f96d7..c07754fedc 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7 github.com/yannh/kubeconform v0.5.0 go.uber.org/zap v1.25.0 - golang.org/x/crypto v0.13.0 + golang.org/x/crypto v0.14.0 golang.org/x/oauth2 v0.7.0 google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.3 @@ -113,7 +113,7 @@ require ( github.com/sirupsen/logrus v1.9.0 // indirect github.com/skeema/knownhosts v1.1.0 // indirect github.com/theckman/yacspin v0.13.12 // indirect - github.com/weaveworks/tf-controller/api v0.0.0-20230416092146-4a7dfa5b6cc4 // indirect + github.com/weaveworks/tf-controller/api v0.0.0-20231101110059-994a65055198 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -204,9 +204,9 @@ require ( go.uber.org/atomic v1.10.0 go.uber.org/multierr v1.10.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.15.0 - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.12.0 + golang.org/x/net v0.17.0 + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.3 // indirect diff --git a/go.sum b/go.sum index 582b2052dc..1f86cc78e0 100644 --- a/go.sum +++ b/go.sum @@ -577,8 +577,8 @@ github.com/tomwright/dasel v1.22.1 h1:Y1wefjI7UlhEsHdOozzp3aGMsipKGGyaft6waUU1m2 github.com/tomwright/dasel v1.22.1/go.mod h1:YmXrjcQHjmOfvG/ZVg7P0hhURwRlqtbNhSWQu0fOeRQ= github.com/weaveworks/policy-agent/api v1.0.5 h1:4pqzzta8xPUsE9h9YhGJVg5XQ2NuAU/CDoU7zdCw5A0= github.com/weaveworks/policy-agent/api v1.0.5/go.mod h1:LlhTiipsV5GSHkL/q7Wa7qrJPZGR9Xtoq2npXTWp9bI= -github.com/weaveworks/tf-controller/api v0.0.0-20230416092146-4a7dfa5b6cc4 h1:+IkLtnXzCkhJzojbadPd+UxwaTa6K/Eb2grY6LcYfeo= -github.com/weaveworks/tf-controller/api v0.0.0-20230416092146-4a7dfa5b6cc4/go.mod h1:LUBkwqS7FHz/QTNuYzvWj6svehhh1djnV0Gj3OTc87E= +github.com/weaveworks/tf-controller/api v0.0.0-20231101110059-994a65055198 h1:lxF3p4ozFKz09MAkN9CO85ZzQm5JRZ8yiXm4S6o4z90= +github.com/weaveworks/tf-controller/api v0.0.0-20231101110059-994a65055198/go.mod h1:201u5xXY+YI7+ggWljE0VvqMxa+zP1Y1lyRXc1RlXBc= github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7 h1:q5qCxFXkRG49L/zL9ArcujuJT+MtQa4swQzAZApwV78= github.com/weaveworks/tf-controller/tfctl v0.0.0-20231025134255-eac574d4bac7/go.mod h1:4DPsiECdMYBva1U9xbRt8+gnyCCc2lRMbyiHrtUOkuo= github.com/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw= @@ -640,8 +640,8 @@ golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -728,8 +728,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -821,8 +821,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -830,8 +830,8 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 2edba72850dc6ea2fd3382946277a50d71349863 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:39:50 +0000 Subject: [PATCH 17/23] build(deps): Bump postcss from 8.4.21 to 8.4.31 in /website Bumps [postcss](https://github.com/postcss/postcss) from 8.4.21 to 8.4.31. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.21...8.4.31) --- updated-dependencies: - dependency-name: postcss dependency-type: indirect ... Signed-off-by: dependabot[bot] --- website/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 658f87380f..7f8b4f55c3 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -5342,10 +5342,10 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== negotiator@0.6.3: version "0.6.3" @@ -6034,11 +6034,11 @@ postcss-zindex@^5.1.0: integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.19: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: - nanoid "^3.3.4" + nanoid "^3.3.6" picocolors "^1.0.0" source-map-js "^1.0.2" From 2c819a2ca6b2bc4d7477341aa9da77efeaf0999a Mon Sep 17 00:00:00 2001 From: yiannis Date: Wed, 1 Nov 2023 14:21:26 +0000 Subject: [PATCH 18/23] chore: Remove GitOps Run components --- go.mod | 17 +- go.sum | 45 -- pkg/run/bootstrap/bootstrap.go | 215 ------ pkg/run/bootstrap/bootstrap_suite_test.go | 34 - pkg/run/bootstrap/bootstrap_wizard.go | 508 ------------ pkg/run/bootstrap/bootstrap_wizard_ui.go | 476 ------------ pkg/run/install/install_fluent_bit.go | 301 -------- pkg/run/install/install_fluent_bit_test.go | 108 --- pkg/run/install/install_vcluster.go | 187 ----- pkg/run/install/install_vcluster_test.go | 60 -- pkg/run/install/session.go | 126 --- pkg/run/session/connect/connect.go | 722 ------------------ pkg/run/session/connect/delete.go | 58 -- pkg/run/session/connect/utils.go | 167 ---- pkg/run/session/find/find.go | 375 --------- pkg/run/session/get.go | 47 -- pkg/run/session/get_test.go | 51 -- pkg/run/session/kubeconfig/kubeconfig.go | 31 - pkg/run/session/list.go | 40 - pkg/run/session/list_test.go | 56 -- pkg/run/session/localkubernetes/configure.go | 412 ---------- .../localkubernetes/localkubernetes.go | 90 --- pkg/run/session/portforward/portforward.go | 459 ----------- pkg/run/session/portforward/start.go | 126 --- pkg/run/session/remove.go | 88 --- pkg/run/watch/install_dev_bucket_server.go | 499 ------------ pkg/run/watch/setup_bucket_source.go | 129 ---- pkg/run/watch/setup_dev_helm.go | 248 ------ pkg/run/watch/setup_dev_ks.go | 538 ------------- pkg/run/watch/setup_dev_ks_test.go | 370 --------- 30 files changed, 1 insertion(+), 6582 deletions(-) delete mode 100644 pkg/run/bootstrap/bootstrap.go delete mode 100644 pkg/run/bootstrap/bootstrap_suite_test.go delete mode 100644 pkg/run/bootstrap/bootstrap_wizard.go delete mode 100644 pkg/run/bootstrap/bootstrap_wizard_ui.go delete mode 100644 pkg/run/install/install_fluent_bit.go delete mode 100644 pkg/run/install/install_fluent_bit_test.go delete mode 100644 pkg/run/install/install_vcluster.go delete mode 100644 pkg/run/install/install_vcluster_test.go delete mode 100644 pkg/run/install/session.go delete mode 100644 pkg/run/session/connect/connect.go delete mode 100644 pkg/run/session/connect/delete.go delete mode 100644 pkg/run/session/connect/utils.go delete mode 100644 pkg/run/session/find/find.go delete mode 100644 pkg/run/session/get.go delete mode 100644 pkg/run/session/get_test.go delete mode 100644 pkg/run/session/kubeconfig/kubeconfig.go delete mode 100644 pkg/run/session/list.go delete mode 100644 pkg/run/session/list_test.go delete mode 100644 pkg/run/session/localkubernetes/configure.go delete mode 100644 pkg/run/session/localkubernetes/localkubernetes.go delete mode 100644 pkg/run/session/portforward/portforward.go delete mode 100644 pkg/run/session/portforward/start.go delete mode 100644 pkg/run/session/remove.go delete mode 100644 pkg/run/watch/install_dev_bucket_server.go delete mode 100644 pkg/run/watch/setup_bucket_source.go delete mode 100644 pkg/run/watch/setup_dev_helm.go delete mode 100644 pkg/run/watch/setup_dev_ks.go delete mode 100644 pkg/run/watch/setup_dev_ks_test.go diff --git a/go.mod b/go.mod index c07754fedc..05b5cd2b74 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,6 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/NYTimes/gziphandler v1.1.1 github.com/alexedwards/scs/v2 v2.5.1 - github.com/charmbracelet/bubbles v0.14.0 - github.com/charmbracelet/bubbletea v0.22.1 - github.com/charmbracelet/lipgloss v0.6.0 github.com/cheshir/ttlcache v1.0.1-0.20220504185148-8ceeff21b789 github.com/coreos/go-oidc/v3 v3.1.0 github.com/fluxcd/go-git-providers v0.16.0 @@ -35,13 +32,11 @@ require ( github.com/mattn/go-tty v0.0.4 github.com/maxbrunsfeld/counterfeiter/v6 v6.4.1 github.com/minio/minio-go/v7 v7.0.31 - github.com/mitchellh/go-ps v1.0.0 github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.10 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 - github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/slok/go-http-metrics v0.10.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.13.0 @@ -71,12 +66,10 @@ require ( require ( github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go v1.44.137 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cloudflare/circl v1.3.3 // indirect - github.com/containerd/console v1.0.3 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/emicklei/go-restful/v3 v3.10.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -93,16 +86,10 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/iancoleman/strcase v0.1.2 // indirect github.com/klauspost/cpuid v1.3.1 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/matryer/is v1.4.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect github.com/minio/md5-simd v1.1.0 // indirect github.com/minio/sha256-simd v0.1.1 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect @@ -141,7 +128,7 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fluxcd/pkg/apis/acl v0.1.0 // indirect github.com/fluxcd/pkg/apis/kustomize v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.6.0 + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect @@ -173,7 +160,6 @@ require ( github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -201,7 +187,6 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect - go.uber.org/atomic v1.10.0 go.uber.org/multierr v1.10.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.17.0 diff --git a/go.sum b/go.sum index 1f86cc78e0..9575fda113 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.137 h1:GH2bUPiW7/gHtB04NxQOSOrKqFNjLGKmqt5YaO+K1SE= github.com/aws/aws-sdk-go v1.44.137/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= @@ -97,15 +95,6 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chanwit/gofakes3 v0.0.0-20220715114300-3f51f1961f7b h1:SDkJpPNFGsrepOwZmnkGmM4/fT6bDeA9R3uarHD3++A= github.com/chanwit/gofakes3 v0.0.0-20220715114300-3f51f1961f7b/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= -github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= -github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= -github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24= -github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= -github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= -github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= github.com/cheshir/ttlcache v1.0.1-0.20220504185148-8ceeff21b789 h1:eWRC5oPQ3G4BtSv0hsHTB777h7iCZct8RCm6jrsozsg= github.com/cheshir/ttlcache v1.0.1-0.20220504185148-8ceeff21b789/go.mod h1:B9qWHhPE7FnRG2HNiPajGzOFX9NYcObDTkg3Ixh9Fzk= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= @@ -124,8 +113,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -382,11 +369,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -405,15 +389,10 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= @@ -431,10 +410,6 @@ github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= -github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -454,17 +429,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= -github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -508,7 +472,6 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -520,9 +483,6 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= -github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= -github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -613,8 +573,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 h1:5/KzhcSqd4UgY51l17r7C5g/JiE6DRw1Vq7VJfQHuMc= go.starlark.net v0.0.0-20221028183056-acb66ad56dd2/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -808,13 +766,10 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/run/bootstrap/bootstrap.go b/pkg/run/bootstrap/bootstrap.go deleted file mode 100644 index b8be3a5074..0000000000 --- a/pkg/run/bootstrap/bootstrap.go +++ /dev/null @@ -1,215 +0,0 @@ -package bootstrap - -import ( - "context" - - "github.com/fluxcd/go-git-providers/github" - "github.com/fluxcd/go-git-providers/gitlab" - "github.com/fluxcd/go-git-providers/gitprovider" - "github.com/fluxcd/go-git-providers/stash" - "github.com/weaveworks/weave-gitops/pkg/fluxexec" - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -type Bootstrap interface { - RunBootstrapCmd(context.Context, *fluxexec.Flux) error - SyncResources(context.Context, logger.Logger, []gitprovider.CommitFile) error -} - -type BootstrapCommon struct { - provider GitProvider - clusterPath string - branch string -} - -// TODO: this needs to be implemented using plain go-git -type BootstrapRaw struct { - BootstrapCommon - url string - password string - privateKeyFile string -} - -type BootstrapForge struct { - BootstrapCommon - isPersonal bool - isPrivate bool - host string - owner string - repository string - user string - pat string -} - -func NewBootstrap(clusterPath string, options BootstrapCmdOptions, provider GitProvider) Bootstrap { - if provider == GitProviderGitHub || provider == GitProviderGitLab || provider == GitProviderBitbucketServer { - return &BootstrapForge{ - BootstrapCommon: BootstrapCommon{ - provider: provider, - clusterPath: clusterPath, - branch: options[BranchOptionKey], - }, - isPersonal: options[PersonalOptionKey] == "true", - isPrivate: options[PrivateOptionKey] == "true", - host: options[HostnameOptionKey], - owner: options[OwnerOptionKey], - repository: options[RepositoryOptionKey], - user: options[UsernameOptionKey], - pat: options[PATOptionKey], - } - } else if provider == GitProviderGit { - return &BootstrapRaw{ - BootstrapCommon: BootstrapCommon{ - provider: provider, - clusterPath: clusterPath, - branch: options[BranchOptionKey], - }, - url: options[URLOptionKey], - password: options[PasswordOptionKey], - privateKeyFile: options[PrivateKeyFileOptionKey], - } - } else { - // TODO put additional manifests on disk - return nil - } -} - -func (b *BootstrapForge) RunBootstrapCmd(ctx context.Context, flux *fluxexec.Flux) error { - globalOptions := fluxexec.WithBootstrapOptions( - fluxexec.Branch(b.branch), - ) - - switch b.provider { - case GitProviderGitHub: - flux.SetEnvVar("GITHUB_TOKEN", b.pat) - - return flux.BootstrapGitHub(ctx, - fluxexec.Hostname(b.host), - fluxexec.Owner(b.owner), - fluxexec.Repository(b.repository), - fluxexec.Path(b.clusterPath), - fluxexec.Personal(b.isPersonal), - fluxexec.Private(b.isPrivate), - fluxexec.WithBootstrapOptions(fluxexec.Branch(b.branch)), - globalOptions, - ) - case GitProviderGitLab: - flux.SetEnvVar("GITLAB_TOKEN", b.pat) - - return flux.BootstrapGitlab(ctx, - fluxexec.Hostname(b.host), - fluxexec.Owner(b.owner), - fluxexec.Repository(b.repository), - fluxexec.Path(b.clusterPath), - fluxexec.Personal(b.isPersonal), - fluxexec.Private(b.isPrivate), - fluxexec.WithBootstrapOptions(fluxexec.Branch(b.branch)), - globalOptions, - ) - case GitProviderBitbucketServer: - flux.SetEnvVar("BITBUCKET_TOKEN", b.pat) - - return flux.BootstrapBitbucketServer(ctx, - fluxexec.Hostname(b.host), - fluxexec.Owner(b.owner), - fluxexec.Repository(b.repository), - fluxexec.Path(b.clusterPath), - fluxexec.Personal(b.isPersonal), - fluxexec.Private(b.isPrivate), - fluxexec.WithBootstrapOptions(fluxexec.Branch(b.branch)), - globalOptions, - ) - } - // unreachable - return nil -} - -func (b *BootstrapForge) SyncResources(ctx context.Context, log logger.Logger, commitFiles []gitprovider.CommitFile) error { - var ( - commits gitprovider.CommitClient - client gitprovider.Client - err error - ) - - switch b.provider { - case GitProviderGitHub: - client, err = github.NewClient( - gitprovider.WithDomain(b.host), - gitprovider.WithOAuth2Token(b.pat), - ) - case GitProviderGitLab: - client, err = gitlab.NewClient(b.pat, "", - gitprovider.WithDomain(b.host), - gitprovider.WithConditionalRequests(true), - ) - case GitProviderBitbucketServer: - client, err = stash.NewStashClient(b.user, b.pat, gitprovider.WithDomain(b.host)) - } - - if err != nil || client == nil { - return err - } - - var repoURL string - - if b.isPersonal { - ref := gitprovider.UserRepositoryRef{ - UserRef: gitprovider.UserRef{ - Domain: b.host, - UserLogin: b.owner, - }, - RepositoryName: b.repository, - } - - repo, err := client.UserRepositories().Get(ctx, ref) - if err != nil { - return err - } - - commits = repo.Commits() - repoURL = ref.String() - } else { - ref := gitprovider.OrgRepositoryRef{ - OrganizationRef: gitprovider.OrganizationRef{ - Domain: b.host, - Organization: b.owner, - // TODO: support suborganizations - }, - RepositoryName: b.repository, - } - repo, err := client.OrgRepositories().Get(ctx, ref) - if err != nil { - return err - } - commits = repo.Commits() - repoURL = ref.String() - } - - _, err = commits.Create(ctx, b.branch, "[gitops run] Additional manifests", commitFiles) - if err != nil { - return err - } - - log.Successf("Your automations have been synced to %v", repoURL) - - return nil -} - -func (b *BootstrapRaw) RunBootstrapCmd(ctx context.Context, flux *fluxexec.Flux) error { - globalOptions := fluxexec.WithBootstrapOptions( - fluxexec.Branch(b.branch), - fluxexec.PrivateKeyFile(b.privateKeyFile), - ) - - return flux.BootstrapGit(ctx, - fluxexec.URL(b.url), - fluxexec.Password(b.password), - fluxexec.Path(b.clusterPath), - globalOptions, - ) -} - -func (b *BootstrapRaw) SyncResources(ctx context.Context, log logger.Logger, commitFiles []gitprovider.CommitFile) error { - // TODO this isn't implemented - return nil -} diff --git a/pkg/run/bootstrap/bootstrap_suite_test.go b/pkg/run/bootstrap/bootstrap_suite_test.go deleted file mode 100644 index 32dd9ad5f3..0000000000 --- a/pkg/run/bootstrap/bootstrap_suite_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package bootstrap - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/pkg/testutils" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - k8sClient client.Client - k8sEnv *testutils.K8sTestEnv -) - -func TestRun(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Run Suite") -} - -var cleanupK8s func() - -var _ = BeforeSuite(func() { - var err error - k8sEnv, err = testutils.StartK8sTestEnvironment([]string{ - "../../manifests/crds", - "../../tools/testcrds", - }) - Expect(err).NotTo(HaveOccurred()) - - cleanupK8s = k8sEnv.Stop - k8sClient = k8sEnv.Client -}) diff --git a/pkg/run/bootstrap/bootstrap_wizard.go b/pkg/run/bootstrap/bootstrap_wizard.go deleted file mode 100644 index 9c8a541959..0000000000 --- a/pkg/run/bootstrap/bootstrap_wizard.go +++ /dev/null @@ -1,508 +0,0 @@ -package bootstrap - -import ( - "fmt" - "os" - "strings" - - tea "github.com/charmbracelet/bubbletea" - gogit "github.com/go-git/go-git/v5" - "github.com/weaveworks/weave-gitops/pkg/logger" -) - -type BootstrapWizardTask struct { - flagName string - flagValue string - defaultFlagValue DefaultValueGetter - flagDescription string - isBoolean bool - isPassword bool -} - -type BootstrapCmdOptions map[string]string - -type BootstrapWizardCmd struct { - Provider GitProvider - Options BootstrapCmdOptions -} - -type BootstrapWizard struct { - gitProvider GitProvider - tasks []*BootstrapWizardTask - cmdOptions BootstrapCmdOptions -} - -const ( - BranchOptionKey = "branch" - HostnameOptionKey = "hostname" - OwnerOptionKey = "owner" - PasswordOptionKey = "password" - PersonalOptionKey = "personal" - PrivateKeyFileOptionKey = "private-key-file" - PrivateOptionKey = "private" - RepositoryOptionKey = "repository" - PATOptionKey = "pat" - URLOptionKey = "url" - UsernameOptionKey = "username" -) - -type DefaultValueGetter func(*gogit.Repository) string - -func GetHost(repo *gogit.Repository) string { - return hostGetter(repo) -} - -func constantDefault(value string) DefaultValueGetter { - return func(_ *gogit.Repository) string { - return value - } -} - -func ownerGetter(repo *gogit.Repository) string { - remoteURL := ParseRemoteURL(repo) - if remoteURL == "" { - return "" - } - - urlParts := GetURLParts(remoteURL) - - return urlParts.owner -} - -func hostGetter(repo *gogit.Repository) string { - remoteURL := ParseRemoteURL(repo) - if remoteURL == "" { - return "" - } - - urlParts := GetURLParts(remoteURL) - - return urlParts.host -} - -func repositoryGetter(repo *gogit.Repository) string { - remoteURL := ParseRemoteURL(repo) - if remoteURL == "" { - return "" - } - - urlParts := GetURLParts(remoteURL) - - return urlParts.repository -} - -func fallbackGetter(getters ...DefaultValueGetter) DefaultValueGetter { - return func(repo *gogit.Repository) string { - for _, getter := range getters { - resp := getter(repo) - if resp != "" { - return resp - } - } - - return "" - } -} - -func branchGetter(repo *gogit.Repository) string { - head, err := repo.Head() - if err != nil { - return "" - } - - branch := head.Name().String() - - if !strings.Contains(branch, branchPrefix) { - return "" - } - - return strings.Replace(branch, branchPrefix, "", 1) -} - -func envGetter(key string) DefaultValueGetter { - return func(_ *gogit.Repository) string { - return os.Getenv(key) - } -} - -var boostrapGitHubTasks = []*BootstrapWizardTask{ - { - flagName: OwnerOptionKey, - flagValue: "", - defaultFlagValue: ownerGetter, - flagDescription: "GitHub user or organization name", - }, - { - flagName: RepositoryOptionKey, - flagValue: "", - defaultFlagValue: repositoryGetter, - flagDescription: "GitHub repository name", - }, - { - flagName: BranchOptionKey, - flagValue: "", - defaultFlagValue: fallbackGetter(branchGetter, constantDefault("main")), - flagDescription: "Git branch", - }, - { - flagName: PersonalOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "if true, the owner is assumed to be a GitHub user; otherwise an org", - isBoolean: true, - }, - { - flagName: PrivateOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "If true, the repository is setup or configured as private", - isBoolean: true, - }, - { - flagName: HostnameOptionKey, - flagValue: "", - defaultFlagValue: hostGetter, - flagDescription: "GitHub hostname", - }, - { - flagName: PATOptionKey, - flagValue: "", - defaultFlagValue: envGetter("GITHUB_TOKEN"), - flagDescription: "GitHub Personal Access Token", - isPassword: true, - }, -} - -var boostrapGitLabTasks = []*BootstrapWizardTask{ - { - flagName: OwnerOptionKey, - flagValue: "", - defaultFlagValue: ownerGetter, - flagDescription: "GitLab user or group name", - }, - { - flagName: RepositoryOptionKey, - flagValue: "", - defaultFlagValue: repositoryGetter, - flagDescription: "GitLab repository name", - }, - { - flagName: BranchOptionKey, - flagValue: "", - defaultFlagValue: branchGetter, - flagDescription: "Git branch (default \"main\")", - }, - { - flagName: PersonalOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "if true, the owner is assumed to be a GitLab user; otherwise a group", - isBoolean: true, - }, - { - flagName: PrivateOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "if true, the repository is setup or configured as private (default true)", - isBoolean: true, - }, - { - flagName: HostnameOptionKey, - flagValue: "", - defaultFlagValue: hostGetter, - flagDescription: "GitLab hostname (default \"gitlab.com\")", - }, - { - flagName: PATOptionKey, - flagValue: "", - defaultFlagValue: envGetter("GITLAB_TOKEN"), - flagDescription: "GitLab Personal Access Token", - isPassword: true, - }, -} - -var boostrapGitTasks = []*BootstrapWizardTask{ - { - flagName: URLOptionKey, - flagValue: "", - flagDescription: "Git repository URL", - }, - { - flagName: PasswordOptionKey, - flagValue: "", - flagDescription: "basic authentication password", - isPassword: true, - }, - { - flagName: PrivateKeyFileOptionKey, - flagValue: "", - flagDescription: "path to a private key file used for authenticating to the Git SSH server", - }, -} - -var boostrapBitbucketServerTasks = []*BootstrapWizardTask{ - { - flagName: OwnerOptionKey, - flagValue: "", - defaultFlagValue: ownerGetter, - flagDescription: "Bitbucket Server user or project name", - }, - { - flagName: UsernameOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("git"), - flagDescription: "authentication username", - }, - { - flagName: RepositoryOptionKey, - flagValue: "", - defaultFlagValue: repositoryGetter, - flagDescription: "Bitbucket Server repository name", - }, - { - flagName: HostnameOptionKey, - flagValue: "", - defaultFlagValue: hostGetter, - flagDescription: "Bitbucket Server hostname", - }, - { - flagName: BranchOptionKey, - flagValue: "", - defaultFlagValue: branchGetter, - flagDescription: "Git branch", - }, - { - flagName: PersonalOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "if true, the owner is assumed to be a Bitbucket Server user; otherwise a group", - isBoolean: true, - }, - { - flagName: PrivateOptionKey, - flagValue: "", - defaultFlagValue: constantDefault("true"), - flagDescription: "if true, the repository is setup or configured as private", - isBoolean: true, - }, - { - flagName: PATOptionKey, - flagValue: "", - defaultFlagValue: envGetter("BITBUCKET_TOKEN"), - flagDescription: "BitBucket Personal Access Token", - isPassword: true, - }, -} - -const ( - providerGitHub = "github.com" - providerGitLab = "gitlab.com" - branchPrefix = "refs/heads/" -) - -type GitProvider int32 - -const ( - GitProviderUnknown GitProvider = 0 - GitProviderGitHub GitProvider = 1 - GitProviderGitLab GitProvider = 2 - GitProviderGit GitProvider = 3 - GitProviderBitbucketServer GitProvider = 4 -) - -const ( - gitProviderGitHubName = "GitHub" - gitProviderGitLabName = "GitLab" - gitProviderGitName = "Git" - gitProviderBitbucketServerName = "BitbucketServer" -) - -var allGitProviderNames = []string{ - gitProviderGitHubName, - gitProviderGitLabName, - gitProviderGitName, - // gitProviderBitbucketServerName, -} - -var allGitProviders = map[string]GitProvider{ - gitProviderGitHubName: GitProviderGitHub, - gitProviderGitLabName: GitProviderGitLab, - gitProviderGitName: GitProviderGit, - gitProviderBitbucketServerName: GitProviderBitbucketServer, -} - -var gitProvidersWithTasks = map[GitProvider][]*BootstrapWizardTask{ - GitProviderGitHub: boostrapGitHubTasks, - GitProviderGitLab: boostrapGitLabTasks, - GitProviderGit: boostrapGitTasks, - GitProviderBitbucketServer: boostrapBitbucketServerTasks, -} - -// ParseRemoteURL extracts remote URL from the repository -func ParseRemoteURL(repo *gogit.Repository) string { - remoteURLs := make(map[string]string) - - if repo != nil { - remotes, _ := repo.Remotes() - - for _, remote := range remotes { - config := remote.Config() - remoteURLs[config.Name] = config.URLs[0] - } - } - - // Return origin first - that's usually the one git cloned from - if url, ok := remoteURLs["origin"]; ok { - return url - } - - // No origin, return "any" - hopefully there's just one - for _, url := range remoteURLs { - return url - } - - // No remotes - return nothing - return "" -} - -// ParseGitProvider extracts git provider from the remote URL, if possible -func ParseGitProvider(hostname string) GitProvider { - provider := GitProviderUnknown - - if hostname == "" { - return provider - } - - if hostname == providerGitHub { - return GitProviderGitHub - } - - if hostname == providerGitLab { - return GitProviderGitLab - } - - return provider -} - -type parts struct { - host string - owner string - repository string -} - -// GetURLParts splits URL to URL parts. -// This takes inspiration from -// https://github.com/fluxcd/go-git-providers/blob/cda93bf5a5fa65bd994a60d6d3ef9ad119cfb684/gitprovider/repositoryref.go#L309 -// except it has to do it in reverse -func GetURLParts(remoteURL string) parts { - sanitizedURL := remoteURL - if strings.HasPrefix(sanitizedURL, "git@") { - sanitizedURL = strings.Replace(sanitizedURL, ":", "/", 1) - } - - replacer := strings.NewReplacer("git@", "", "https://", "", ".git", "", "ssh://", "") - - sanitizedURL = replacer.Replace(sanitizedURL) - - urlParts := strings.Split(sanitizedURL, "/") - - return parts{ - host: urlParts[0], - repository: urlParts[len(urlParts)-1], - owner: strings.Join(urlParts[1:len(urlParts)-1], "/"), - } -} - -// NewBootstrapWizard creates a wizard to gather -// all bootstrap config options before running flux bootstrap. -func NewBootstrapWizard(log logger.Logger, gitProvider GitProvider, repo *gogit.Repository) (*BootstrapWizard, error) { - if gitProvider == GitProviderUnknown { - return nil, fmt.Errorf("unknown git provider: %d", gitProvider) - } - - wizard := &BootstrapWizard{ - gitProvider: gitProvider, - tasks: []*BootstrapWizardTask{}, - } - - tasks := gitProvidersWithTasks[gitProvider] - - wizard.tasks = make([]*BootstrapWizardTask, len(tasks)) - copy(wizard.tasks, tasks) - - log.Actionf("Parsing values ...") - - if repo == nil { - return wizard, nil - } - - for _, task := range wizard.tasks { - if task.flagValue == "" && task.defaultFlagValue != nil { - task.flagValue = task.defaultFlagValue(repo) - continue - } - } - - return wizard, nil -} - -// ParseGitRemote parses the git remote (if it exists) -// from the working directory to autofill some command options. -func ParseGitRemote(log logger.Logger, workingDir string) (*gogit.Repository, error) { - log.Actionf("Collecting information about Git remote ...") - - if workingDir == "" { - return nil, fmt.Errorf("unable to parse Git remote for empty workingDir") - } - - if _, err := os.Stat(workingDir); err != nil { - return nil, fmt.Errorf("error validating workingDir %s: %w", workingDir, err) - } - - repo, err := gogit.PlainOpen(workingDir) - if err != nil { - return nil, fmt.Errorf("error parsing Git remote for workingDir %s: %w", workingDir, err) - } - - return repo, nil -} - -// SelectGitProvider displays text inputs to enter or edit all command flag values. -func SelectGitProvider(log logger.Logger) (GitProvider, error) { - provider := GitProviderUnknown - - m := initialPreWizardModel(make(chan GitProvider)) - - err := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion()).Start() - if err != nil { - return provider, fmt.Errorf("could not start tea program: %v", err.Error()) - } - - provider = <-m.msgChan - - return provider, nil -} - -// Run displays text inputs to enter or edit all command flag values. -func (wizard *BootstrapWizard) Run(log logger.Logger) error { - log.Actionf("Please enter or edit command values...") - - m := initialWizardModel(wizard.tasks, make(chan BootstrapCmdOptions)) - - err := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion()).Start() - if err != nil { - return fmt.Errorf("could not start tea program: %v", err.Error()) - } - - wizard.cmdOptions = <-m.msgChan - - return nil -} - -// BuildCmd builds flux bootstrap command options as key/values pairs. -func (wizard *BootstrapWizard) BuildCmd(log logger.Logger) BootstrapWizardCmd { - return BootstrapWizardCmd{ - Provider: wizard.gitProvider, - Options: wizard.cmdOptions, - } -} diff --git a/pkg/run/bootstrap/bootstrap_wizard_ui.go b/pkg/run/bootstrap/bootstrap_wizard_ui.go deleted file mode 100644 index a2c0d9f291..0000000000 --- a/pkg/run/bootstrap/bootstrap_wizard_ui.go +++ /dev/null @@ -1,476 +0,0 @@ -package bootstrap - -import ( - "fmt" - "strconv" - "strings" - - "github.com/charmbracelet/bubbles/table" - "github.com/charmbracelet/bubbles/textinput" - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -type preWizardModel struct { - windowIsReady bool - viewport viewport.Model - table table.Model - textInput textinput.Model - msgChan chan GitProvider -} - -type wizardModel struct { - windowIsReady bool - viewport viewport.Model - inputs []*bootstrapWizardInput - msgChan chan BootstrapCmdOptions - cursorMode textinput.CursorMode - focusIndex int - errorMsg string -} - -type checkbox struct { - checked bool -} - -type bootstrapWizardInputType int32 - -const ( - bootstrapWizardInputTypeTextInput bootstrapWizardInputType = 0 - bootstrapWizardInputTypeCheckbox bootstrapWizardInputType = 1 -) - -type bootstrapWizardInput struct { - inputType bootstrapWizardInputType - flagName string - prompt string - textInput textinput.Model - checkboxInput *checkbox -} - -const ( - flagSeparator = " - " - buttonText = "Submit" -) - -// UI styling -var ( - // table - baseTableStyle = lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("240")) - - // text inputs - focusedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) - blurredStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("240")) - - cursorStyle = focusedStyle.Copy() - noStyle = lipgloss.NewStyle() - helpStyle = blurredStyle.Copy() - cursorModeHelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("244")) - - focusedButton = focusedStyle.Render(fmt.Sprintf("[ %s ]", buttonText)) - blurredButton = fmt.Sprintf("[ %s ]", blurredStyle.Render(buttonText)) -) - -func makeViewport(width, height int, content string) viewport.Model { - vp := viewport.New(width, height) - vp.YPosition = 0 - vp.SetContent(content) - - return vp -} - -func initialPreWizardModel(msgChan chan GitProvider) preWizardModel { - columns := []table.Column{ - {Title: "Index", Width: 6}, - {Title: "Git Provider", Width: 20}, - } - - rows := []table.Row{} - - for i, name := range allGitProviderNames { - rows = append(rows, []string{ - fmt.Sprint(i + 1), name, - }) - } - - t := table.New( - table.WithColumns(columns), - table.WithRows(rows), - table.WithHeight(len(allGitProviders)), - ) - - s := table.DefaultStyles() - s.Header = s.Header. - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("240")). - BorderBottom(true). - Bold(false) - s.Selected = s.Selected.Bold(false).Foreground(lipgloss.NoColor{}) - t.SetStyles(s) - - ti := textinput.New() - ti.Placeholder = "Please enter your Git provider index or name from the table" - ti.Focus() - ti.CharLimit = 120 - ti.Width = 40 - ti.PromptStyle = focusedStyle - ti.TextStyle = focusedStyle - - return preWizardModel{ - table: t, - textInput: ti, - msgChan: msgChan, - } -} - -func (m preWizardModel) Init() tea.Cmd { return textinput.Blink } - -func (m preWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.Type { - case tea.KeyCtrlC: - return m, tea.Quit - case tea.KeyEnter: - provider := GitProviderUnknown - - indexOrName := strings.ToLower(strings.TrimSpace(m.textInput.Value())) - - for key, value := range allGitProviders { - strValue := fmt.Sprint(value) - - if indexOrName == strings.ToLower(strValue) || indexOrName == strings.ToLower(key) { - provider = allGitProviders[key] - break - } - } - - if provider != GitProviderUnknown { - go func() { m.msgChan <- provider }() - - return m, tea.Quit - } - } - case tea.WindowSizeMsg: - if !m.windowIsReady { - m.viewport = makeViewport(msg.Width, msg.Height, m.getContent()) - - m.windowIsReady = true - } else { - m.viewport.Width = msg.Width - m.viewport.Height = msg.Height - } - } - - var ( - cmdViewport tea.Cmd - cmdTable tea.Cmd - cmdTextInput tea.Cmd - ) - - m.table, cmdTable = m.table.Update(msg) - m.textInput, cmdTextInput = m.textInput.Update(msg) - - m.viewport.SetContent(m.getContent()) - - m.viewport, cmdViewport = m.viewport.Update(msg) - cmds := []tea.Cmd{cmdViewport, cmdTable, cmdTextInput} - - return m, tea.Batch(cmds...) -} - -func (m preWizardModel) View() string { - return m.viewport.View() -} - -func (m preWizardModel) getContent() string { - return fmt.Sprintf( - "Please enter Git provider index or name and press Enter"+"\n"+ - "(up and down arrows to scroll the view,"+"\n"+ - "Ctrl+C twice to quit):"+"\n%s", - baseTableStyle.Render(m.table.View())+"\n", - ) + m.textInput.View() -} - -func makeInput(task *BootstrapWizardTask, isFocused bool) *bootstrapWizardInput { - var inputType bootstrapWizardInputType - - if task.isBoolean { - inputType = bootstrapWizardInputTypeCheckbox - } else { - inputType = bootstrapWizardInputTypeTextInput - } - - flagName := task.flagName - - prompt := task.flagName + flagSeparator + task.flagDescription - - ti := textinput.Model{} - - var cb *checkbox - - if inputType == bootstrapWizardInputTypeCheckbox { - cb = &checkbox{ - checked: task.flagValue == "true", - } - } else { - ti = textinput.New() - ti.CursorStyle = cursorStyle - ti.CharLimit = 100 - - ti.SetValue(task.flagValue) - ti.Placeholder = task.flagDescription - - if task.isPassword { - ti.EchoMode = textinput.EchoPassword - } - - if isFocused { - ti.Focus() - ti.PromptStyle = focusedStyle - ti.TextStyle = focusedStyle - } - } - - return &bootstrapWizardInput{ - inputType: inputType, - flagName: flagName, - prompt: prompt, - textInput: ti, - checkboxInput: cb, - } -} - -func (input *bootstrapWizardInput) getView(isFocused bool) string { - if input.inputType == bootstrapWizardInputTypeTextInput { - return input.textInput.View() - } - - var checkmark string - - if input.checkboxInput.checked { - checkmark = "x" - - if isFocused { - checkmark = focusedStyle.Render(checkmark) - } - } else { - checkmark = blurredStyle.Render("_") - } - - checkboxStart := "[" - checkboxEnd := "]" - - if isFocused { - checkboxStart = focusedStyle.Render(checkboxStart) - checkboxEnd = focusedStyle.Render(checkboxEnd) - } - - return fmt.Sprintf("%s%s%s %s", checkboxStart, checkmark, checkboxEnd, input.flagName) -} - -func initialWizardModel(tasks []*BootstrapWizardTask, msgChan chan BootstrapCmdOptions) wizardModel { - numInputs := len(tasks) - - inputs := make([]*bootstrapWizardInput, numInputs) - - for i := range inputs { - inputs[i] = makeInput(tasks[i], i == 0) - } - - return wizardModel{ - inputs: inputs, - errorMsg: "", - msgChan: msgChan, - } -} - -func (m wizardModel) Init() tea.Cmd { - return textinput.Blink -} - -func (m wizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmds []tea.Cmd - - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.Type { - case tea.KeyCtrlC, tea.KeyEsc: - return m, tea.Quit - - // Change cursor mode - case tea.KeyCtrlR: - m.cursorMode++ - if m.cursorMode > textinput.CursorHide { - m.cursorMode = textinput.CursorBlink - } - - cmdsTextInputs := make([]tea.Cmd, len(m.inputs)) - - for i, input := range m.inputs { - if input.inputType == bootstrapWizardInputTypeTextInput { - cmdsTextInputs[i] = input.textInput.SetCursorMode(m.cursorMode) - } - } - - cmds = append(cmds, cmdsTextInputs...) - case tea.KeyTab, tea.KeyShiftTab, tea.KeyEnter: - t := msg.Type - - if t == tea.KeyEnter && m.focusIndex == len(m.inputs) { - options := make(BootstrapCmdOptions) - - for _, input := range m.inputs { - var value string - - if input.inputType == bootstrapWizardInputTypeTextInput { - value = strings.TrimSpace(input.textInput.Value()) - - if value == "" { - m.errorMsg = "Missing value in " + input.textInput.Placeholder - - m.viewport.SetContent(m.getContent()) - - var cmdViewport tea.Cmd - - m.viewport, cmdViewport = m.viewport.Update(msg) - - return m, cmdViewport - } - } else { - value = strconv.FormatBool(input.checkboxInput.checked) - } - - options[input.flagName] = value - } - - go func() { m.msgChan <- options }() - - return m, tea.Quit - } - - if t == tea.KeyShiftTab { - m.focusIndex-- - } else { - m.focusIndex++ - } - - if m.focusIndex > len(m.inputs) { - m.focusIndex = 0 - } else if m.focusIndex < 0 { - m.focusIndex = len(m.inputs) - } - - cmdsTextInputs := []tea.Cmd{} - - for i, input := range m.inputs[:len(m.inputs)] { - if input.inputType == bootstrapWizardInputTypeCheckbox { - continue - } - - if i == m.focusIndex { - cmdsTextInputs = append(cmdsTextInputs, input.textInput.Focus()) - input.textInput.PromptStyle = focusedStyle - input.textInput.TextStyle = focusedStyle - - continue - } - - input.textInput.Blur() - input.textInput.PromptStyle = noStyle - input.textInput.TextStyle = noStyle - } - - cmds = append(cmds, cmdsTextInputs...) - case tea.KeySpace: - if m.focusIndex == len(m.inputs) { - return m, nil - } - - input := m.inputs[m.focusIndex] - - if input.inputType != bootstrapWizardInputTypeCheckbox { - break - } - - input.checkboxInput.checked = !input.checkboxInput.checked - } - case tea.WindowSizeMsg: - if !m.windowIsReady { - m.viewport = makeViewport(msg.Width, msg.Height, m.getContent()) - - m.windowIsReady = true - } else { - m.viewport.Width = msg.Width - m.viewport.Height = msg.Height - } - } - - cmdsTextInputs := m.updateInputs(msg) - - m.viewport.SetContent(m.getContent()) - - var cmdViewport tea.Cmd - - m.viewport, cmdViewport = m.viewport.Update(msg) - - cmds = append(cmds, cmdsTextInputs, cmdViewport) - - return m, tea.Batch(cmds...) -} - -func (m wizardModel) View() string { - return m.viewport.View() -} - -func (m *wizardModel) updateInputs(msg tea.Msg) tea.Cmd { - cmds := make([]tea.Cmd, len(m.inputs)) - - // Only text inputs with Focus() set will respond, so it's safe to simply - // update all of them here without any further logic. - for i, input := range m.inputs { - if input.inputType == bootstrapWizardInputTypeCheckbox { - continue - } - - input.textInput, cmds[i] = input.textInput.Update(msg) - } - - return tea.Batch(cmds...) -} - -func (m wizardModel) getContent() string { - var b strings.Builder - - b.WriteString("Please enter the following values" + "\n" + - "(Tab and Shift+Tab to move input selection," + "\n" + - "(Space to toggle the currently focused checkbox," + "\n" + - "Enter to move to the next input or submit the form, " + "\n" + - "up and down arrows to scroll the view, Ctrl+C twice to quit):" + "\n\n\n") - - for i, input := range m.inputs { - b.WriteString(input.prompt) - b.WriteRune('\n') - b.WriteString(input.getView(i == m.focusIndex)) - - if i < len(m.inputs)-1 { - b.WriteRune('\n') - } - } - - button := &blurredButton - if m.focusIndex == len(m.inputs) { - button = &focusedButton - } - - fmt.Fprintf(&b, "\n\n%s %s\n\n", *button, m.errorMsg) - - b.WriteString(helpStyle.Render("cursor mode is ")) - b.WriteString(cursorModeHelpStyle.Render(m.cursorMode.String())) - b.WriteString(helpStyle.Render(" (Ctrl+R to change style)")) - - return b.String() -} diff --git a/pkg/run/install/install_fluent_bit.go b/pkg/run/install/install_fluent_bit.go deleted file mode 100644 index 7126c3bfd9..0000000000 --- a/pkg/run/install/install_fluent_bit.go +++ /dev/null @@ -1,301 +0,0 @@ -package install - -import ( - "context" - "encoding/json" - "fmt" - "html/template" - "strings" - "time" - - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" - - helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const FluentBitHRName = "fluent-bit" - -const configInputs = ` -[INPUT] - Name tail - Path /var/log/containers/*.log - multiline.parser docker, cri - Tag kube.* - Mem_Buf_Limit 5MB - Skip_Long_Lines Off -` - -const configFilters = ` -[FILTER] - Name kubernetes - Match kube.* - Merge_Log On - Keep_Log Off - K8S-Logging.Parser On - K8S-Logging.Exclude On -[FILTER] - Name grep - Match * - Exclude $kubernetes['namespace_name'] (gitops-run|kube-system) -[FILTER] - Name grep - Match * - Exclude $kubernetes['pod_name'] ^fluent\-bit -` - -func makeConfigOutputs(bucket string, port int32) (string, error) { - if port < 1 || port > 65535 { - return "", fmt.Errorf("port %d not between 1 and 65535", port) - } - - const configOutputs = ` -[OUTPUT] - Name s3 - Match kube.* - bucket {{ .Bucket }} - endpoint http://run-dev-bucket.gitops-run.svc:{{ .Port }} - tls Off - tls.verify Off - use_put_object true - preserve_data_ordering true - static_file_path true - total_file_size 1M - upload_timeout 15s - s3_key_format /fluent-bit-logs/$TAG[4].%Y%m%d%H%M%S -` - tmpl, err := template.New("configOutputs").Parse(strings.TrimSpace(configOutputs)) - if err != nil { - return "", fmt.Errorf("error parsing template: %v", err) - } - var result strings.Builder - err = tmpl.Execute(&result, struct { - Bucket string - Port int32 - }{Bucket: bucket, Port: port}) - if err != nil { - return "", fmt.Errorf("error executing template: %v", err) - } - return result.String(), nil -} - -func mapToJSON(m map[string]interface{}) (*v1.JSON, error) { - // convert the map to JSON - b, err := json.Marshal(m) - if err != nil { - return nil, err - } - - // create a new JSON struct - result := &v1.JSON{Raw: b} - return result, nil -} - -func makeFluentBitHelmRepository(namespace string) *sourcev1b2.HelmRepository { - helmRepository := &sourcev1b2.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fluent", - Namespace: namespace, - }, - Spec: sourcev1b2.HelmRepositorySpec{ - URL: "https://fluent.github.io/helm-charts", - }, - } - - return helmRepository -} - -func makeFluentBitHelmRelease(name, fluxNamespace, targetNamespace, bucketName string, bucketServerPort int32) (*helmv2.HelmRelease, error) { - configOutputs, err := makeConfigOutputs(bucketName, bucketServerPort) - if err != nil { - return nil, err - } - - values, err := mapToJSON(map[string]interface{}{ - "env": []map[string]interface{}{ - { - "name": "AWS_ACCESS_KEY_ID", - "valueFrom": map[string]interface{}{ - "secretKeyRef": map[string]interface{}{ - "name": constants.RunDevBucketCredentials, - "key": "accesskey", - }, - }, - }, - { - "name": "AWS_SECRET_ACCESS_KEY", - "valueFrom": map[string]interface{}{ - "secretKeyRef": map[string]interface{}{ - "name": constants.RunDevBucketCredentials, - "key": "secretkey", - }, - }, - }, - }, - "config": map[string]interface{}{ - "inputs": strings.TrimSpace(configInputs), - "filters": strings.TrimSpace(configFilters), - "outputs": configOutputs, - }, - }) - if err != nil { - return nil, err - } - - obj := helmv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: fluxNamespace, - }, - Spec: helmv2.HelmReleaseSpec{ - Chart: helmv2.HelmChartTemplate{ - Spec: helmv2.HelmChartTemplateSpec{ - Chart: "fluent-bit", - SourceRef: helmv2.CrossNamespaceObjectReference{ - Kind: "HelmRepository", - Name: "fluent", - Namespace: fluxNamespace, - }, - Version: "*", - }, - }, - Interval: metav1.Duration{Duration: 1 * time.Hour}, - ReleaseName: name, - TargetNamespace: targetNamespace, - Install: &helmv2.Install{ - CRDs: helmv2.Create, - }, - Upgrade: &helmv2.Upgrade{ - CRDs: helmv2.CreateReplace, - }, - Values: values, - }, - } - - return &obj, nil -} - -func UninstallFluentBit(ctx context.Context, log logger.Logger, kubeClient client.Client, hrNamespace, hrName string) error { - hr := &helmv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: hrName, - Namespace: hrNamespace, - }, - } - - log.Actionf("Removing Fluent Bit HelmRelease %s/%s ...", hr.Namespace, hr.Name) - - if err := kubeClient.Delete(ctx, hr); err != nil { - if !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete HelmRelease: %w", err) - } - } - - log.Actionf("Waiting for HelmRelease %s/%s to be deleted...", hr.Namespace, hr.Name) - - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: 4 * time.Second, - Factor: 2, - Jitter: 1, - Steps: 10, - }, func() (done bool, err error) { - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(hr), hr); err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } else { - return false, fmt.Errorf("failed retrieving HelmRelease: %w", err) - } - } - return false, nil - }); err != nil { - return fmt.Errorf("failed waiting for HelmRelease %s/%s to be deleted: %w", hr.Namespace, hr.Name, err) - } - - log.Successf("HelmRelease %s/%s deleted", hr.Namespace, hr.Name) - - helmRepository := &sourcev1b2.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fluent", - Namespace: hrNamespace, - }, - } - - if err := kubeClient.Delete(ctx, helmRepository); err != nil { - if !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete HelmRepository: %w", err) - } - } - - log.Successf("HelmRepository %s/%s deleted", helmRepository.Namespace, helmRepository.Name) - - return nil -} - -func InstallFluentBit(ctx context.Context, log logger.Logger, kubeClient client.Client, fluxNamespace, targetNamespace, name, bucketName string, bucketServerPort int32) error { - helmRepo := makeFluentBitHelmRepository(fluxNamespace) - - log.Actionf("creating HelmRepository %s/%s", helmRepo.Namespace, helmRepo.Name) - - if err := kubeClient.Create(ctx, helmRepo); err != nil { - if apierrors.IsAlreadyExists(err) { - // do nothing - } else { - return err - } - } - - log.Actionf("creating HelmRelease %s/%s", fluxNamespace, name) - - helmRelease, err := makeFluentBitHelmRelease(name, fluxNamespace, targetNamespace, bucketName, bucketServerPort) - if err != nil { - return err - } - - log.Actionf("creating HelmRelease %s/%s", helmRelease.Namespace, helmRelease.Name) - - if err := kubeClient.Create(ctx, helmRelease); err != nil { - if apierrors.IsAlreadyExists(err) { - // do nothing - } else { - return err - } - } - - log.Actionf("waiting for HelmRelease %s/%s to be ready", helmRelease.Namespace, helmRelease.Name) - - if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { - instance := appsv1.DaemonSet{} - if err := kubeClient.Get( - ctx, - types.NamespacedName{ - Name: name, - Namespace: targetNamespace, - }, &instance); err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } else { - return false, err - } - } - - if instance.Status.NumberReady >= 1 { - return true, nil - } - - return false, nil - }); err != nil { - log.Failuref("HelmRelease %s/%s failed to become ready", helmRelease.Namespace, helmRelease.Name) - return err - } - - log.Successf("HelmRelease %s/%s is ready", helmRelease.Namespace, helmRelease.Name) - return nil -} diff --git a/pkg/run/install/install_fluent_bit_test.go b/pkg/run/install/install_fluent_bit_test.go deleted file mode 100644 index f2530de758..0000000000 --- a/pkg/run/install/install_fluent_bit_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package install - -import ( - "encoding/json" - "fmt" - "strings" - "testing" - - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/pkg/run/constants" -) - -func TestMakeConfigOutputs(t *testing.T) { - g := NewGomegaWithT(t) - - tests := []struct { - name string - bucket string - port int32 - expected string - err error - }{ - { - name: "valid input", - bucket: "test-bucket", - port: 8080, - expected: "[OUTPUT]\n Name s3\n Match kube.*\n bucket test-bucket\n endpoint http://run-dev-bucket.gitops-run.svc:8080\n tls Off\n tls.verify Off\n use_put_object true\n preserve_data_ordering true\n static_file_path true\n total_file_size 1M\n upload_timeout 15s\n s3_key_format /fluent-bit-logs/$TAG[4].%Y%m%d%H%M%S", - err: nil, - }, - { - name: "invalid port - too low", - bucket: "test-bucket", - port: 0, - expected: "", - err: fmt.Errorf("port 0 not between 1 and 65535"), - }, - { - name: "invalid port - too high", - bucket: "test-bucket", - port: 65536, - expected: "", - err: fmt.Errorf("port 65536 not between 1 and 65535"), - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - result, err := makeConfigOutputs(test.bucket, test.port) - if test.err != nil { - g.Expect(err).To(MatchError(test.err)) - } else { - g.Expect(err).To(BeNil()) - } - g.Expect(result).To(Equal(test.expected)) - }) - } -} - -func TestMakeFluentBitHelmRelease(t *testing.T) { - g := NewGomegaWithT(t) - - name := "test-release" - namespace := "test-ns" - targetNamespace := "test-target-ns" - bucketName := "test-bucket" - bucketServerPort := int32(9000) - - configOutputs, err := makeConfigOutputs(bucketName, bucketServerPort) - g.Expect(err).To(BeNil()) - - expectedValues, _ := json.Marshal(map[string]interface{}{ - "env": []map[string]interface{}{ - { - "name": "AWS_ACCESS_KEY_ID", - "valueFrom": map[string]interface{}{ - "secretKeyRef": map[string]interface{}{ - "name": constants.RunDevBucketCredentials, - "key": "accesskey", - }, - }, - }, - { - "name": "AWS_SECRET_ACCESS_KEY", - "valueFrom": map[string]interface{}{ - "secretKeyRef": map[string]interface{}{ - "name": constants.RunDevBucketCredentials, - "key": "secretkey", - }, - }, - }, - }, - "config": map[string]interface{}{ - "inputs": strings.TrimSpace(configInputs), - "filters": strings.TrimSpace(configFilters), - "outputs": configOutputs, - }, - }) - - release, err := makeFluentBitHelmRelease(name, namespace, targetNamespace, bucketName, bucketServerPort) - g.Expect(err).To(BeNil()) - g.Expect(release.Name).To(Equal(name)) - g.Expect(release.Namespace).To(Equal(namespace)) - g.Expect(release.Spec.Chart.Spec.Chart).To(Equal("fluent-bit")) - g.Expect(release.Spec.Chart.Spec.Version).To(Equal("*")) - g.Expect(release.Spec.Chart.Spec.SourceRef.Kind).To(Equal("HelmRepository")) - g.Expect(release.Spec.Chart.Spec.SourceRef.Name).To(Equal("fluent")) - g.Expect(release.Spec.Chart.Spec.SourceRef.Namespace).To(Equal("test-ns")) - g.Expect(release.Spec.Values.Raw).To(MatchJSON(expectedValues)) -} diff --git a/pkg/run/install/install_vcluster.go b/pkg/run/install/install_vcluster.go deleted file mode 100644 index df42ef86df..0000000000 --- a/pkg/run/install/install_vcluster.go +++ /dev/null @@ -1,187 +0,0 @@ -package install - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" - - helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" - "github.com/weaveworks/weave-gitops/cmd/gitops/version" - coretypes "github.com/weaveworks/weave-gitops/core/server/types" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - "github.com/weaveworks/weave-gitops/pkg/run/session" - appsv1 "k8s.io/api/apps/v1" - apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func makeVClusterHelmRepository(namespace string) *sourcev1b2.HelmRepository { - helmRepository := &sourcev1b2.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "loft-sh", - Namespace: namespace, - }, - Spec: sourcev1b2.HelmRepositorySpec{ - URL: "https://charts.loft.sh", - }, - } - - return helmRepository -} - -func makeVClusterHelmRelease(name, namespace, fluxNamespace, command string, portForwards []string, automationKind string) *helmv2.HelmRelease { - annotations := []string{ - `"run.weave.works/cli-version": "` + version.Version + `"`, - `"run.weave.works/command": "` + command + `"`, - `"run.weave.works/automation-kind": "` + automationKind + `"`, - `"run.weave.works/namespace": "` + namespace + `"`, - `"run.weave.works/flux-namespace": "` + fluxNamespace + `"`, - } - - if len(portForwards) > 0 { - annotations = append(annotations, `"run.weave.works/port-forward":"`+strings.Join(portForwards, ",")+`"`) - } - - helmRelease := &helmv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - coretypes.AppLabel: "vcluster", - coretypes.PartOfLabel: "gitops-run", - }, - }, - Spec: helmv2.HelmReleaseSpec{ - Chart: helmv2.HelmChartTemplate{ - Spec: helmv2.HelmChartTemplateSpec{ - Chart: "vcluster", - SourceRef: helmv2.CrossNamespaceObjectReference{ - Kind: "HelmRepository", - Name: "loft-sh", - }, - }, - }, - Interval: metav1.Duration{Duration: 1 * time.Hour}, - ReleaseName: name, - TargetNamespace: namespace, - Install: &helmv2.Install{ - CRDs: helmv2.Create, - }, - Upgrade: &helmv2.Upgrade{ - CRDs: helmv2.CreateReplace, - }, - Values: &apiextensions.JSON{Raw: []byte(fmt.Sprintf(` -{ - "labels": { - "app.kubernetes.io/part-of": "gitops-run" - }, - "annotations": { - %s - }, - "hostpathMapper": { - "enabled": true - }, - "mapServices": { - "fromVirtual": [ - { - "from": "%s/%s", - "to": "%s-bucket" - } - ] - } -}`, - strings.Join(annotations, ", \n "), - constants.GitOpsRunNamespace, - constants.RunDevBucketName, - name, - ))}, - }, - } - - return helmRelease -} - -func installVCluster(kubeClient client.Client, name, namespace, fluxNamespace string, portForwards []string, automationKind string) error { - helmRepo := makeVClusterHelmRepository(namespace) - - if err := kubeClient.Create(context.Background(), helmRepo); err != nil { - if apierrors.IsAlreadyExists(err) { - // do nothing - } else { - return err - } - } - - args := append([]string{filepath.Base(os.Args[0])}, os.Args[1:]...) - command := strings.Join(args, " ") - - helmRelease := makeVClusterHelmRelease(name, namespace, fluxNamespace, command, portForwards, automationKind) - - if err := kubeClient.Create(context.Background(), helmRelease); err != nil { - if apierrors.IsAlreadyExists(err) { - // do nothing - } else { - return err - } - } - - if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { - instance := appsv1.StatefulSet{} - if err := kubeClient.Get( - context.Background(), - types.NamespacedName{ - Name: name, - Namespace: namespace, - }, &instance); err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } else { - return false, err - } - } - - if instance.Status.ReadyReplicas >= 1 { - return true, nil - } - - return false, nil - }); err != nil { - return err - } - - return nil -} - -func uninstallVcluster(kubeClient client.Client, name, namespace string) error { - // clean up the session resources using functions from the session package - internalSession, err := session.Get(kubeClient, name, namespace) - if err != nil { - return err - } - - if err := session.Remove(kubeClient, internalSession); err != nil { - return err - } - - // clean up repo - helmRepo := makeVClusterHelmRepository(namespace) - - if err := kubeClient.Delete(context.Background(), helmRepo); err != nil { - if apierrors.IsNotFound(err) { - // Do nothing because the helmRepo can be deleted by other GitOps Run instances - } else { - return err - } - } - - return nil -} diff --git a/pkg/run/install/install_vcluster_test.go b/pkg/run/install/install_vcluster_test.go deleted file mode 100644 index 231b2e6e97..0000000000 --- a/pkg/run/install/install_vcluster_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package install - -import ( - "encoding/json" - "testing" - - . "github.com/onsi/gomega" -) - -func TestMakeVClusterHelmReleaseAnnotations(t *testing.T) { - tests := []struct { - name string - portForwards []string - expectedAnnotations map[string]string - unexpectedAnnotations []string - }{ - { - name: "port-forwards are properly annotated", - portForwards: []string{"9999", "1111"}, - expectedAnnotations: map[string]string{ - "run.weave.works/port-forward": "9999,1111", - "run.weave.works/automation-kind": "automationKind", - "run.weave.works/namespace": "namespace", - "run.weave.works/command": "command", - }, - }, - { - name: "port-forwards are left out", - unexpectedAnnotations: []string{"run.weave.works/port-forward"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewGomegaWithT(t) - hl := makeVClusterHelmRelease( - "name", - "namespace", - "flux-system", - "command", tt.portForwards, - "automationKind") - - g.Expect(hl.Name).To(Equal("name")) - g.Expect(hl.Namespace).To(Equal("namespace")) - - values := map[string]interface{}{} - g.Expect(json.Unmarshal(hl.Spec.Values.Raw, &values)).ToNot(HaveOccurred()) - - annotations := values["annotations"].(map[string]interface{}) - - for k, v := range tt.expectedAnnotations { - g.Expect(annotations).To(HaveKeyWithValue(k, v)) - } - - for _, k := range tt.unexpectedAnnotations { - g.Expect(annotations).NotTo(HaveKey(k)) - } - }) - } -} diff --git a/pkg/run/install/session.go b/pkg/run/install/session.go deleted file mode 100644 index 243dc3dd17..0000000000 --- a/pkg/run/install/session.go +++ /dev/null @@ -1,126 +0,0 @@ -package install - -import ( - "os" - "os/signal" - "syscall" - - "github.com/mitchellh/go-ps" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/session/connect" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type Session struct { - name string - namespace string - fluxNamespace string - kubeClient client.Client - log logger.Logger - dashboardHashedPassword string - skipDashboardInstall bool - portForwards []string - automationKind string -} - -func (s *Session) Start() error { - if err := installVCluster(s.kubeClient, s.name, s.namespace, s.fluxNamespace, s.portForwards, s.automationKind); err != nil { - return err - } - - return nil -} - -func (s *Session) Connect() error { - subProcArgs := append(os.Args, - // we must run the sub-process without a session. - "--no-session", - // we must let the sub-run know that this is the session name of the sub-process - "--x-session-name", s.name, - // vclusters are always new clusters, that doesn't mean we haven't bootstrapped the outer cluster. - "--no-bootstrap", - // allow the sub-process to connect to the vcluster context. - "--allow-k8s-context="+s.name, - // we must skip resource cleanup in the sub-process because we are already deleting the vcluster. - // it's for optimization purposes. - "--skip-resource-cleanup", - ) - - if s.skipDashboardInstall { - // we skip dashboard install in the sub-process. - subProcArgs = append(subProcArgs, "--skip-dashboard-install") - } else if s.dashboardHashedPassword != "" { - // we forward dashboard password from host to session too. - subProcArgs = append(subProcArgs, "--dashboard-hashed-password="+s.dashboardHashedPassword) - } - - // we support statefulset pod only for now. - conn := &connect.Connection{ - PodName: s.name + "-0", - Log: s.log, - Namespace: s.namespace, - BackgroundProxy: false, - KubeConfigContextName: s.name, - } - - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - - go func() { - sig := <-c - signal.Reset(sig) - - thisProc := os.Getpid() - allProcesses, err := ps.Processes() - - if err != nil { - return - } - - for _, proc := range allProcesses { - if proc.PPid() == thisProc { - // ok it's a child process, obtain the process object - procObject, err := os.FindProcess(proc.Pid()) - if err != nil { - continue - } - - // and notify it - if err := procObject.Signal(syscall.SIGUSR1); err != nil { - return - } - } - } - }() - - err := conn.Connect(s.name, subProcArgs) - - return err -} - -func (s *Session) Close() error { - if err := uninstallVcluster(s.kubeClient, s.name, s.namespace); err != nil { - return err - } - - return nil -} - -func NewSession(log logger.Logger, - kubeClient client.Client, - name string, namespace string, - fluxNamespace string, portForwards []string, - skipDashboardInstall bool, dashboardHashedPassword string, - automationKind string) (*Session, error) { - return &Session{ - name: name, - namespace: namespace, - fluxNamespace: fluxNamespace, - kubeClient: kubeClient, - log: log, - portForwards: portForwards, - skipDashboardInstall: skipDashboardInstall, - dashboardHashedPassword: dashboardHashedPassword, - automationKind: automationKind, - }, nil -} diff --git a/pkg/run/session/connect/connect.go b/pkg/run/session/connect/connect.go deleted file mode 100644 index 3d14420a4d..0000000000 --- a/pkg/run/session/connect/connect.go +++ /dev/null @@ -1,722 +0,0 @@ -package connect - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "os" - "os/exec" - "os/signal" - "sort" - "strconv" - "strings" - "syscall" - "time" - - "github.com/pkg/errors" - log "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/session/find" - "github.com/weaveworks/weave-gitops/pkg/run/session/localkubernetes" - "github.com/weaveworks/weave-gitops/pkg/run/session/portforward" - authenticationv1 "k8s.io/api/authentication/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" -) - -type Connection struct { - Address string - BackgroundProxy bool - Context string - Insecure bool - KubeConfig string - KubeConfigContextName string - LocalPort int - Log log.Logger - Namespace string - PodName string - Print bool - Server string - ServiceAccount string - ServiceAccountClusterRole string - ServiceAccountExpiration int - UpdateCurrent bool - - errorChan chan error - interruptChan chan struct{} - kubeClient *kubernetes.Clientset - kubeClientConfig clientcmd.ClientConfig - portForwarding bool - rawConfig api.Config - restConfig *rest.Config -} - -func (conn *Connection) Connect(vclusterName string, command []string) error { - if vclusterName == "" { - return fmt.Errorf("vcluster name is required") - } - - if conn.PodName == "" { - return fmt.Errorf("pod name is required") - } - - // prepare clients and find vcluster - err := conn.prepare(vclusterName) - if err != nil { - return err - } - - // retrieve vcluster kube config - kubeConfig, err := conn.getVClusterKubeConfig(vclusterName, command) - if err != nil { - return err - } - - if len(command) == 0 && conn.ServiceAccount == "" && conn.Server == "" && conn.BackgroundProxy && localkubernetes.IsDockerInstalledAndUpAndRunning() { - // start background container - server, err := localkubernetes.CreateBackgroundProxyContainer(vclusterName, conn.Namespace, &conn.rawConfig, kubeConfig, conn.LocalPort, conn.Log) - if err != nil { - conn.Log.Warningf("Error exposing local vcluster, will fallback to port-forwarding: %v", err) - conn.BackgroundProxy = false - } - conn.Server = server - } - - // check if we should execute command - if len(command) > 0 { - return conn.executeCommand(*kubeConfig, command) - } - - // write kube config to buffer - out, err := clientcmd.Write(*kubeConfig) - if err != nil { - return err - } - - // write kube config to file - if conn.UpdateCurrent { - var clusterConfig *api.Cluster - for _, c := range kubeConfig.Clusters { - clusterConfig = c - } - - var authConfig *api.AuthInfo - for _, a := range kubeConfig.AuthInfos { - authConfig = a - } - - err = updateKubeConfig(conn.KubeConfigContextName, clusterConfig, authConfig, true) - if err != nil { - return err - } - - conn.Log.Successf("Switched active kube context to %s", conn.KubeConfigContextName) - if !conn.BackgroundProxy && conn.portForwarding { - conn.Log.Warningf("Since you are using port-forwarding to connect, you will need to leave this terminal open") - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - <-c - kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() - if err == nil && kubeConfig.CurrentContext == conn.KubeConfigContextName { - err = deleteContext(&kubeConfig, conn.KubeConfigContextName, conn.Context) - if err != nil { - conn.Log.Failuref("Error deleting context: %v", err) - } else { - conn.Log.Actionf("Switched back to context %v", conn.Context) - } - } - os.Exit(1) - }() - - defer func() { - signal.Stop(c) - }() - conn.Log.Println("- Use CTRL+C to return to your previous kube context\n") - conn.Log.Println("- Use `kubectl get namespaces` in another terminal to access the vcluster\n") - } else { - conn.Log.Println("- Use `vcluster disconnect` to return to your previous kube context\n") - conn.Log.Println("- Use `kubectl get namespaces` to access the vcluster\n") - } - } else if conn.Print { - _, err = os.Stdout.Write(out) - if err != nil { - return err - } - } else { - err = os.WriteFile(conn.KubeConfig, out, 0o666) - if err != nil { - return errors.Wrap(err, "write kube config") - } - - conn.Log.Successf("Virtual cluster kube config written to: %s", conn.KubeConfig) - if conn.Server == "" { - conn.Log.Println(fmt.Sprintf("- Use `vcluster connect %s -n %s -- kubectl get ns` to execute a command directly within this terminal\n", vclusterName, conn.Namespace)) - } - conn.Log.Println(fmt.Sprintf("- Use `kubectl --kubeconfig %s get namespaces` to access the vcluster\n", conn.KubeConfig)) - } - - // wait for port-forwarding if necessary - if conn.portForwarding { - if conn.Server != "" { - // Stop port-forwarding here - close(conn.interruptChan) - } - - return <-conn.errorChan - } - - return nil -} - -func (conn *Connection) executeCommand(vKubeConfig api.Config, command []string) error { - if !conn.portForwarding { - return fmt.Errorf("command is specified, but port-forwarding isn't started") - } - - defer close(conn.interruptChan) - - // wait for vcluster to be ready - err := conn.waitForVCluster(vKubeConfig, conn.errorChan) - if err != nil { - return err - } - - // convert to local kube config - vKubeConfig = conn.getLocalVClusterConfig(vKubeConfig) - out, err := clientcmd.Write(vKubeConfig) - if err != nil { - return err - } - - // write a temporary kube file - tempFile, err := os.CreateTemp("", "") - if err != nil { - return errors.Wrap(err, "create temp file") - } - defer func(name string) { - _ = os.Remove(name) - }(tempFile.Name()) - - _, err = tempFile.Write(out) - if err != nil { - return errors.Wrap(err, "write kube config to temp file") - } - - err = tempFile.Close() - if err != nil { - return errors.Wrap(err, "close temp file") - } - - commandErrChan := make(chan error) - execCmd := exec.Command(command[0], command[1:]...) - execCmd.Env = os.Environ() - execCmd.Env = append(execCmd.Env, "KUBECONFIG="+tempFile.Name()) - execCmd.Stdout = os.Stdout - execCmd.Stdin = os.Stdin - execCmd.Stderr = os.Stderr - err = execCmd.Start() - if err != nil { - return err - } - go func() { - commandErrChan <- execCmd.Wait() - }() - - select { - case err := <-conn.errorChan: - if execCmd.Process != nil { - _ = execCmd.Process.Kill() - } - - return errors.Wrap(err, "error port-forwarding") - case err := <-commandErrChan: - if exitError, ok := err.(*exec.ExitError); ok { - conn.Log.Failuref("Error executing command: %v", err) - os.Exit(exitError.ExitCode()) //nolint:gocritic - } - - return err - } -} - -func (conn *Connection) prepare(vclusterName string) error { - if conn.LocalPort == 0 { - conn.LocalPort = randomPort() - } - - if conn.ServiceAccountClusterRole != "" && conn.ServiceAccount == "" { - return fmt.Errorf("expected service-account to be defined as well") - } - - var ( - kubeConfigLoader clientcmd.ClientConfig - vCluster *find.VCluster - err error - ) - if vclusterName != "" { - vCluster, err = find.GetVCluster(conn.Context, vclusterName, conn.Namespace) - if err != nil { - return err - } - - kubeConfigLoader = vCluster.ClientFactory - conn.Context = vCluster.Context - conn.Namespace = vCluster.Namespace - } else { - kubeConfigLoader = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{ - CurrentContext: conn.Context, - }) - } - - restConfig, err := kubeConfigLoader.ClientConfig() - if err != nil { - return errors.Wrap(err, "load kube config") - } - kubeClient, err := kubernetes.NewForConfig(restConfig) - if err != nil { - return errors.Wrap(err, "create kube client") - } - rawConfig, err := kubeConfigLoader.RawConfig() - if err != nil { - return errors.Wrap(err, "load raw config") - } - rawConfig.CurrentContext = conn.Context - - conn.kubeClient = kubeClient - conn.restConfig = restConfig - conn.kubeClientConfig = kubeConfigLoader - conn.rawConfig = rawConfig - - // set the namespace correctly - if conn.Namespace == "" { - conn.Namespace, _, err = kubeConfigLoader.Namespace() - if err != nil { - return err - } - } - - return nil -} - -func (conn *Connection) waitForVCluster(vKubeConfig api.Config, errorChan chan error) error { - vKubeClient, err := conn.getLocalVClusterClient(vKubeConfig) - if err != nil { - return err - } - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - err = wait.PollImmediate(time.Millisecond*200, time.Minute*3, func() (bool, error) { - select { - case err := <-errorChan: - return false, err - default: - // check if service account exists - _, err = vKubeClient.CoreV1().ServiceAccounts("default").Get(context.TODO(), "default", metav1.GetOptions{}) - return err == nil, nil - } - }) - if err != nil { - return errors.Wrap(err, "wait for vcluster to become ready") - } - - return nil -} - -func (conn *Connection) getVClusterKubeConfig(vclusterName string, command []string) (*api.Config, error) { - var err error - podName := conn.PodName - if podName == "" { - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, time.Second*6, func() (bool, error) { - // get vcluster pod name - var pods *corev1.PodList - pods, err = conn.kubeClient.CoreV1().Pods(conn.Namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: "app=vcluster,release=" + vclusterName, - }) - if err != nil { - return false, err - } else if len(pods.Items) == 0 { - err = fmt.Errorf("can't find a running vcluster pod in namespace %s", conn.Namespace) - return false, nil - } - - // sort by newest - sort.Slice(pods.Items, func(i, j int) bool { - return pods.Items[i].CreationTimestamp.Unix() > pods.Items[j].CreationTimestamp.Unix() - }) - if pods.Items[0].DeletionTimestamp != nil { - err = fmt.Errorf("can't find a running vcluster pod in namespace %s", conn.Namespace) - return false, nil - } - - podName = pods.Items[0].Name - return true, nil - }) - if waitErr != nil { - return nil, fmt.Errorf("finding vcluster pod: %v - %v", waitErr, err) - } - } - - // get the kube config from the Secret - kubeConfig, err := GetKubeConfig(context.Background(), conn.kubeClient, vclusterName, conn.Namespace, conn.Log) - if err != nil { - return nil, errors.Wrap(err, "failed to parse kube config") - } - - // find out port we should listen to locally - if len(kubeConfig.Clusters) != 1 { - return nil, fmt.Errorf("unexpected kube config") - } - - // exchange context name in virtual kube config - conn.exchangeContextName(kubeConfig, vclusterName) - - // check if the vcluster is exposed and set server - if vclusterName != "" && conn.Server == "" && len(command) == 0 { - err = conn.setServerIfExposed(vclusterName, kubeConfig) - if err != nil { - return nil, err - } - } - - // find out vcluster server port - port := "8443" - for k := range kubeConfig.Clusters { - if conn.Insecure { - kubeConfig.Clusters[k].CertificateAuthorityData = nil - kubeConfig.Clusters[k].InsecureSkipTLSVerify = true - } - - if conn.Server != "" { - if !strings.HasPrefix(conn.Server, "https://") { - conn.Server = "https://" + conn.Server - } - - kubeConfig.Clusters[k].Server = conn.Server - } else { - parts := strings.Split(kubeConfig.Clusters[k].Server, ":") - if len(parts) != 3 { - return nil, fmt.Errorf("unexpected server in kubeconfig: %s", kubeConfig.Clusters[k].Server) - } - - port = parts[2] - parts[2] = strconv.Itoa(conn.LocalPort) - kubeConfig.Clusters[k].Server = strings.Join(parts, ":") - } - } - - // start port forwarding - if conn.ServiceAccount != "" || conn.Server == "" || len(command) > 0 { - conn.portForwarding = true - conn.interruptChan = make(chan struct{}) - conn.errorChan = make(chan error) - - // silence port-forwarding if a command is used - stdout := io.Writer(os.Stdout) - stderr := io.Writer(os.Stderr) - if len(command) > 0 || conn.BackgroundProxy { - stdout = io.Discard - stderr = io.Discard - } - - go func() { - conn.errorChan <- portforward.StartPortForwardingWithRestart(conn.restConfig, conn.Address, podName, conn.Namespace, strconv.Itoa(conn.LocalPort), port, conn.interruptChan, stdout, stderr, conn.Log) - }() - } - - // we want to use a service account token in the kube config - if conn.ServiceAccount != "" { - token, err := conn.createServiceAccountToken(*kubeConfig) - if err != nil { - return nil, err - } - - // set service account token - for k := range kubeConfig.AuthInfos { - kubeConfig.AuthInfos[k] = &api.AuthInfo{ - Token: token, - Extensions: make(map[string]runtime.Object), - ImpersonateUserExtra: make(map[string][]string), - } - } - } - - return kubeConfig, nil -} - -func (conn *Connection) setServerIfExposed(vClusterName string, vClusterConfig *api.Config) error { - printedWaiting := false - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - err := wait.PollImmediate(time.Second*2, time.Minute*5, func() (done bool, err error) { - service, err := conn.kubeClient.CoreV1().Services(conn.Namespace).Get(context.TODO(), vClusterName, metav1.GetOptions{}) - if err != nil { - if kerrors.IsNotFound(err) { - return true, nil - } - - return false, err - } - - // not a load balancer? Then don't wait - if service.Spec.Type == corev1.ServiceTypeNodePort { - server, err := localkubernetes.ExposeLocal(vClusterName, conn.Namespace, &conn.rawConfig, vClusterConfig, service, conn.LocalPort, conn.Log) - if err != nil { - conn.Log.Warningf("Error exposing local vcluster, will fallback to port-forwarding: %v", err) - } - - conn.Server = server - return true, nil - } else if service.Spec.Type != corev1.ServiceTypeLoadBalancer { - return true, nil - } - - if len(service.Status.LoadBalancer.Ingress) == 0 { - if !printedWaiting { - conn.Log.Actionf("Waiting for vcluster LoadBalancer ip...") - printedWaiting = true - } - - return false, nil - } - - if service.Status.LoadBalancer.Ingress[0].Hostname != "" { - conn.Server = service.Status.LoadBalancer.Ingress[0].Hostname - } else if service.Status.LoadBalancer.Ingress[0].IP != "" { - conn.Server = service.Status.LoadBalancer.Ingress[0].IP - } - - if conn.Server == "" { - return false, nil - } - - conn.Log.Actionf("Using vcluster %s load balancer endpoint: %s", vClusterName, conn.Server) - return true, nil - }) - if err != nil { - return errors.Wrap(err, "wait for vcluster") - } - - return nil -} - -func (conn *Connection) exchangeContextName(kubeConfig *api.Config, vclusterName string) { - if conn.KubeConfigContextName == "" { - if vclusterName != "" { - conn.KubeConfigContextName = find.VClusterContextName(vclusterName, conn.Namespace, conn.rawConfig.CurrentContext) - } else { - conn.KubeConfigContextName = find.VClusterContextName(conn.PodName, conn.Namespace, conn.rawConfig.CurrentContext) - } - } - - // update cluster - for k := range kubeConfig.Clusters { - kubeConfig.Clusters[conn.KubeConfigContextName] = kubeConfig.Clusters[k] - delete(kubeConfig.Clusters, k) - break - } - - // update context - for k := range kubeConfig.Contexts { - ctx := kubeConfig.Contexts[k] - ctx.Cluster = conn.KubeConfigContextName - ctx.AuthInfo = conn.KubeConfigContextName - kubeConfig.Contexts[conn.KubeConfigContextName] = ctx - delete(kubeConfig.Contexts, k) - break - } - - // update authInfo - for k := range kubeConfig.AuthInfos { - kubeConfig.AuthInfos[conn.KubeConfigContextName] = kubeConfig.AuthInfos[k] - delete(kubeConfig.AuthInfos, k) - break - } - - // update current-context - kubeConfig.CurrentContext = conn.KubeConfigContextName -} - -func (conn *Connection) getLocalVClusterConfig(vKubeConfig api.Config) api.Config { - // wait until we can access the virtual cluster - vKubeConfig = *vKubeConfig.DeepCopy() - for k := range vKubeConfig.Clusters { - vKubeConfig.Clusters[k].Server = "https://localhost:" + strconv.Itoa(conn.LocalPort) - } - return vKubeConfig -} - -func (conn *Connection) getLocalVClusterClient(vKubeConfig api.Config) (kubernetes.Interface, error) { - vRestConfig, err := clientcmd.NewDefaultClientConfig(conn.getLocalVClusterConfig(vKubeConfig), &clientcmd.ConfigOverrides{}).ClientConfig() - if err != nil { - return nil, errors.Wrap(err, "create virtual rest config") - } - - vKubeClient, err := kubernetes.NewForConfig(vRestConfig) - if err != nil { - return nil, errors.Wrap(err, "create virtual kube client") - } - - return vKubeClient, nil -} - -func SafeConcatName(name ...string) string { - fullPath := strings.Join(name, "-") - if len(fullPath) > 63 { - digest := sha256.Sum256([]byte(fullPath)) - return strings.ReplaceAll(fullPath[0:52]+"-"+hex.EncodeToString(digest[0:])[0:10], ".-", "-") - } - return fullPath -} - -func (conn *Connection) createServiceAccountToken(vKubeConfig api.Config) (string, error) { - vKubeClient, err := conn.getLocalVClusterClient(vKubeConfig) - if err != nil { - return "", err - } - - var ( - serviceAccount = conn.ServiceAccount - serviceAccountNamespace = "kube-system" - ) - if strings.Contains(conn.ServiceAccount, "/") { - splitted := strings.Split(conn.ServiceAccount, "/") - if len(splitted) != 2 { - return "", fmt.Errorf("unexpected service account reference, expected ServiceAccountNamespace/ServiceAccountName") - } - - serviceAccountNamespace = splitted[0] - serviceAccount = splitted[1] - } - - audiences := []string{"https://kubernetes.default.svc.cluster.local", "https://kubernetes.default.svc", "https://kubernetes.default"} - expirationSeconds := int64(10 * 365 * 24 * 60 * 60) - if conn.ServiceAccountExpiration > 0 { - expirationSeconds = int64(conn.ServiceAccountExpiration) - } - token := "" - conn.Log.Actionf("Create service account token for %s/%s", serviceAccountNamespace, serviceAccount) - - err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute*3, true, func(_ context.Context) (bool, error) { - // check if namespace exists - _, err := vKubeClient.CoreV1().Namespaces().Get(context.TODO(), serviceAccountNamespace, metav1.GetOptions{}) - if err != nil { - if kerrors.IsNotFound(err) || kerrors.IsForbidden(err) { - return false, err - } - - return false, nil - } - - // check if service account exists - _, err = vKubeClient.CoreV1().ServiceAccounts(serviceAccountNamespace).Get(context.TODO(), serviceAccount, metav1.GetOptions{}) - if err != nil { - if kerrors.IsNotFound(err) { - if serviceAccount == "default" { - return false, nil - } - - if conn.ServiceAccountClusterRole != "" { - // create service account - _, err = vKubeClient.CoreV1().ServiceAccounts(serviceAccountNamespace).Create(context.TODO(), &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccount, - Namespace: serviceAccountNamespace, - }, - }, metav1.CreateOptions{}) - if err != nil { - return false, err - } - - conn.Log.Successf("Created service account %s/%s", serviceAccountNamespace, serviceAccount) - } else { - return false, err - } - } else if kerrors.IsForbidden(err) { - return false, err - } else { - return false, nil - } - } - - // create service account cluster role binding - if conn.ServiceAccountClusterRole != "" { - clusterRoleBindingName := SafeConcatName("vcluster", "sa", serviceAccount, serviceAccountNamespace) - clusterRoleBinding, err := vKubeClient.RbacV1().ClusterRoleBindings().Get(context.TODO(), clusterRoleBindingName, metav1.GetOptions{}) - if err != nil { - if kerrors.IsNotFound(err) { - // create cluster role binding - _, err = vKubeClient.RbacV1().ClusterRoleBindings().Create(context.TODO(), &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: clusterRoleBindingName, - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.SchemeGroupVersion.Group, - Kind: "ClusterRole", - Name: conn.ServiceAccountClusterRole, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: serviceAccount, - Namespace: serviceAccountNamespace, - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - return false, err - } - - conn.Log.Successf("Created cluster role binding for cluster role %s", conn.ServiceAccountClusterRole) - } else if kerrors.IsForbidden(err) { - return false, err - } else { - return false, nil - } - } else { - // if cluster role differs, recreate it - if clusterRoleBinding.RoleRef.Name != conn.ServiceAccountClusterRole { - err = vKubeClient.RbacV1().ClusterRoleBindings().Delete(context.TODO(), clusterRoleBindingName, metav1.DeleteOptions{}) - if err != nil { - return false, err - } - - conn.Log.Successf("Recreate cluster role binding for service account") - // this will recreate the cluster role binding in the next iteration - return false, nil - } - } - } - - // create service account token - result, err := vKubeClient.CoreV1().ServiceAccounts(serviceAccountNamespace).CreateToken(context.TODO(), serviceAccount, &authenticationv1.TokenRequest{Spec: authenticationv1.TokenRequestSpec{ - Audiences: audiences, - ExpirationSeconds: &expirationSeconds, - }}, metav1.CreateOptions{}) - if err != nil { - if kerrors.IsNotFound(err) || kerrors.IsForbidden(err) { - return false, err - } - - return false, nil - } - - token = result.Status.Token - return true, nil - }) - if err != nil { - return "", errors.Wrap(err, "create service account token") - } - - return token, nil -} diff --git a/pkg/run/session/connect/delete.go b/pkg/run/session/connect/delete.go deleted file mode 100644 index 4bbe2d2c56..0000000000 --- a/pkg/run/session/connect/delete.go +++ /dev/null @@ -1,58 +0,0 @@ -package connect - -import ( - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" -) - -func deleteContext(kubeConfig *clientcmdapi.Config, kubeContext, otherContext string) error { - // Get context - contextRaw, ok := kubeConfig.Contexts[kubeContext] - if !ok { - return nil - } - - // Remove context - delete(kubeConfig.Contexts, kubeContext) - - removeAuthInfo := true - removeCluster := true - - // Check if AuthInfo or Cluster is used by any other context - for name, ctx := range kubeConfig.Contexts { - if name != kubeContext && ctx.AuthInfo == contextRaw.AuthInfo { - removeAuthInfo = false - } - - if name != kubeContext && ctx.Cluster == contextRaw.Cluster { - removeCluster = false - } - } - - // Remove AuthInfo if not used by any other context - if removeAuthInfo { - delete(kubeConfig.AuthInfos, contextRaw.AuthInfo) - } - - // Remove Cluster if not used by any other context - if removeCluster { - delete(kubeConfig.Clusters, contextRaw.Cluster) - } - - if kubeConfig.CurrentContext == kubeContext { - kubeConfig.CurrentContext = "" - - if otherContext != "" { - kubeConfig.CurrentContext = otherContext - } else if len(kubeConfig.Contexts) > 0 { - for contextName, contextObj := range kubeConfig.Contexts { - if contextObj != nil { - kubeConfig.CurrentContext = contextName - break - } - } - } - } - - return clientcmd.ModifyConfig(clientcmd.NewDefaultClientConfigLoadingRules(), *kubeConfig, false) -} diff --git a/pkg/run/session/connect/utils.go b/pkg/run/session/connect/utils.go deleted file mode 100644 index ce9f4e5f27..0000000000 --- a/pkg/run/session/connect/utils.go +++ /dev/null @@ -1,167 +0,0 @@ -package connect - -import ( - "context" - "math/rand" - "net" - "sort" - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - log "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/session/find" - "github.com/weaveworks/weave-gitops/pkg/run/session/kubeconfig" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" -) - -// CriticalStatus container status -var CriticalStatus = map[string]bool{ - "Error": true, - "Unknown": true, - "ImagePullBackOff": true, - "CrashLoopBackOff": true, - "RunContainerError": true, - "ErrImagePull": true, - "CreateContainerConfigError": true, - "InvalidImageName": true, -} - -var SortPodsByNewest = func(pods []corev1.Pod, i, j int) bool { - return pods[i].CreationTimestamp.Unix() > pods[j].CreationTimestamp.Unix() -} - -// GetKubeConfig attempts to read the kubeconfig from the default Secret and -// falls back to reading from filesystem if the Secret is not read successfully. -// Reading from filesystem is implemented for the backward compatibility and -// can be eventually removed in the future. -// -// This is retried until the kube config is successfully retrieve, or until 10 minute timeout is reached. -func GetKubeConfig(ctx context.Context, kubeClient *kubernetes.Clientset, vclusterName, namespace string, log log.Logger) (*api.Config, error) { - var kubeConfig *api.Config - - printedWaiting := false - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - err := wait.PollImmediate(time.Second, time.Minute*10, func() (done bool, err error) { - podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: "app=vcluster,release=" + vclusterName, - }) - if err != nil { - return false, err - } else if len(podList.Items) > 0 { - sort.Slice(podList.Items, func(i, j int) bool { - return SortPodsByNewest(podList.Items, i, j) - }) - if HasPodProblem(&podList.Items[0]) { - if !printedWaiting { - log.Actionf("Waiting for vcluster to come up...") - printedWaiting = true - } - - return false, nil - } else if !allContainersReady(&podList.Items[0]) { - if !printedWaiting { - log.Actionf("Waiting for vcluster to come up...") - printedWaiting = true - } - - return false, nil - } - } - - kubeConfig, err = kubeconfig.ReadKubeConfig(ctx, kubeClient, vclusterName, namespace) - if err != nil { - if !printedWaiting { - log.Actionf("Waiting for vcluster to come up...") - printedWaiting = true - } - - return false, nil - } - return true, nil - }) - if err != nil { - return nil, errors.Wrap(err, "wait for vcluster") - } - - return kubeConfig, nil -} - -func allContainersReady(pod *corev1.Pod) bool { - for _, cs := range pod.Status.ContainerStatuses { - if !cs.Ready || cs.State.Running == nil { - return false - } - } - return true -} - -func HasPodProblem(pod *corev1.Pod) bool { - status := find.GetPodStatus(pod) - status = strings.TrimPrefix(status, "Init:") - return CriticalStatus[status] -} - -func updateKubeConfig(contextName string, cluster *api.Cluster, authInfo *api.AuthInfo, setActive bool) error { - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() - if err != nil { - return err - } - - config.Clusters[contextName] = cluster - config.AuthInfos[contextName] = authInfo - - // Update kube context - newContext := api.NewContext() - newContext.Cluster = contextName - newContext.AuthInfo = contextName - - config.Contexts[contextName] = newContext - if setActive { - config.CurrentContext = contextName - } - - // Save the config - return clientcmd.ModifyConfig(clientcmd.NewDefaultClientConfigLoadingRules(), config, false) -} - -func randomPort() int { - srand := rand.New(rand.NewSource(time.Now().UnixNano())) - - for i := 0; i < 10; i++ { - port := 10000 + srand.Intn(3000) - s, err := checkPort(port) - if s && err == nil { - return port - } - } - - // just try another port - return 10000 + srand.Intn(3000) -} - -func checkPort(port int) (status bool, err error) { - // Concatenate a colon and the port - host := "localhost:" + strconv.Itoa(port) - - // Try to create a server with the port - server, err := net.Listen("tcp", host) - - // if it fails then the port is likely taken - if err != nil { - return false, err - } - - // close the server - _ = server.Close() - - // we successfully used and closed the port - // so it's now available to be used again - return true, nil -} diff --git a/pkg/run/session/find/find.go b/pkg/run/session/find/find.go deleted file mode 100644 index c04962020f..0000000000 --- a/pkg/run/session/find/find.go +++ /dev/null @@ -1,375 +0,0 @@ -package find - -import ( - "context" - "fmt" - "os" - "strings" - "time" - - "github.com/pkg/errors" - log "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const VirtualClusterSelector = "app=vcluster" - -type VCluster struct { - Name string - Namespace string - - Status Status - Created metav1.Time - Context string - ClientFactory clientcmd.ClientConfig `json:"-"` -} - -type Status string - -const ( - StatusPaused Status = "Paused" - StatusUnknown Status = "Unknown" -) - -func CurrentContext() (string, *api.Config, error) { - rawConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() - if err != nil { - return "", nil, err - } - - return rawConfig.CurrentContext, &rawConfig, nil -} - -func GetVCluster(context, name, namespace string) (*VCluster, error) { - if name == "" { - return nil, fmt.Errorf("please specify a name") - } - - vclusters, err := ListVClusters(context, name, namespace) - if err != nil { - return nil, err - } else if len(vclusters) == 0 { - return nil, fmt.Errorf("couldn't find vcluster %s", name) - } else if len(vclusters) == 1 { - return &vclusters[0], nil - } - - return nil, fmt.Errorf("multiple vclusters with name %s found, please specify a namespace via -n", name) -} - -func ListVClusters(context, name, namespace string) ([]VCluster, error) { - if context == "" { - var err error - context, _, err = CurrentContext() - if err != nil { - return nil, err - } - } - - vClusterName, _, vClusterContext := VClusterFromContext(context) - timeout := time.Minute - if vClusterName != "" { - timeout = time.Second * 5 - } - - vclusters, err := findInContext(context, name, namespace, timeout, false) - // In case of error in vcluster listing in vcluster context, the below check will skip the error and try searching for parent context vclusters. - if err != nil && vClusterName == "" { - return nil, errors.Wrap(err, "find vcluster") - } - - if vClusterName != "" { - parentContextVclusters, err := findInContext(vClusterContext, name, namespace, time.Minute, true) - if err != nil { - return nil, errors.Wrap(err, "find vcluster") - } - - vclusters = append(vclusters, parentContextVclusters...) - } - - return vclusters, nil -} - -func VClusterContextName(vClusterName, vClusterNamespace, currentContext string) string { - return "vcluster_" + vClusterName + "_" + vClusterNamespace + "_" + currentContext -} - -func VClusterConnectBackgroundProxyName(vClusterName, vClusterNamespace, currentContext string) string { - return VClusterContextName(vClusterName, vClusterNamespace, currentContext) + "_background_proxy" -} - -func VClusterFromContext(originalContext string) (name, namespace, context string) { - if !strings.HasPrefix(originalContext, "vcluster_") { - return "", "", "" - } - - splitted := strings.Split(originalContext, "_") - if len(splitted) < 4 { - return "", "", "" - } - - return splitted[1], splitted[2], strings.Join(splitted[3:], "_") -} - -func findInContext(context, name, namespace string, timeout time.Duration, isParentContext bool) ([]VCluster, error) { - vclusters := []VCluster{} - kubeClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{ - CurrentContext: context, - }) - restConfig, err := kubeClientConfig.ClientConfig() - if err != nil { - // we can ignore this error for parent context, it just means that the kubeconfig set doesn't have parent config in it. - if isParentContext { - logger := log.NewCLILogger(os.Stdout) - logger.Warningf("parent context unreachable - No vclusters listed from parent context") - return vclusters, nil - } - return nil, errors.Wrap(err, "load kube config") - } - kubeClient, err := kubernetes.NewForConfig(restConfig) - if err != nil { - return nil, errors.Wrap(err, "create kube client") - } - - // statefulset based vclusters - statefulSets, err := getStatefulSets(kubeClient, namespace, kubeClientConfig, timeout) - if err != nil { - return nil, err - } - for _, p := range statefulSets.Items { - if release, ok := p.Labels["release"]; ok { - if name != "" && name != release { - continue - } - - var paused string - - if p.Annotations != nil { - paused = p.Annotations[constants.PausedAnnotation] - } - if p.Spec.Replicas != nil && *p.Spec.Replicas == 0 && paused != "true" { - // if the stateful set has been scaled down we'll ignore it -- this happens when - // using devspace to do vcluster plugin dev for example, devspace scales down the - // vcluster stateful set and re-creates a deployment for "dev mode" so we end up - // with a duplicate vcluster in the list, one for the statefulset and one for the - // deployment. Of course if the vcluster is paused (via `vcluster pause`), we *do* - // still need to care about it even if replicas == 0. - - continue - } - - vCluster, err := getVCluster(&p, context, release, kubeClient, kubeClientConfig) - if err != nil { - return nil, err - } - vCluster.Context = context - vclusters = append(vclusters, vCluster) - } - } - - // deployment based vclusters - deployments, err := getDeployments(kubeClient, namespace, kubeClientConfig, timeout) - if err != nil { - return nil, err - } - for _, p := range deployments.Items { - if release, ok := p.Labels["release"]; ok { - if name != "" && name != release { - continue - } - - vCluster, err2 := getVCluster(&p, context, release, kubeClient, kubeClientConfig) - if err2 != nil { - return nil, err2 - } - - vCluster.Context = context - vclusters = append(vclusters, vCluster) - } - } - - return vclusters, nil -} - -func getVCluster(object client.Object, context, release string, client *kubernetes.Clientset, kubeClientConfig clientcmd.ClientConfig) (VCluster, error) { - namespace := object.GetNamespace() - created := object.GetCreationTimestamp() - releaseName := "" - status := "" - if object.GetAnnotations() != nil && object.GetAnnotations()[constants.PausedAnnotation] == "true" { - status = string(StatusPaused) - } else { - releaseName = "release=" + release - } - - if status == "" { - pods, err := getPods(client, kubeClientConfig, namespace, releaseName) - if err != nil { - return VCluster{}, err - } - for _, pod := range pods.Items { - status = GetPodStatus(&pod) - } - } - if status == "" { - status = string(StatusUnknown) - } - - return VCluster{ - Name: release, - Namespace: namespace, - Status: Status(status), - Created: created, - Context: context, - ClientFactory: kubeClientConfig, - }, nil -} - -func getPods(client *kubernetes.Clientset, kubeClientConfig clientcmd.ClientConfig, namespace, podSelector string) (*corev1.PodList, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - podList, err := client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector + "," + podSelector, - }) - if err != nil { - if kerrors.IsForbidden(err) { - // try the current namespace instead - if namespace, err = getAccessibleNS(kubeClientConfig); err != nil { - return nil, err - } - return client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector, - }) - } - return nil, err - } - return podList, nil -} - -func getDeployments(client *kubernetes.Clientset, namespace string, kubeClientConfig clientcmd.ClientConfig, timeout time.Duration) (*appsv1.DeploymentList, error) { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - deploymentList, err := client.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector, - }) - if err != nil { - if kerrors.IsForbidden(err) { - // try the current namespace instead - if namespace, err = getAccessibleNS(kubeClientConfig); err != nil { - return nil, err - } - return client.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector, - }) - } - return nil, err - } - return deploymentList, nil -} - -func getStatefulSets(client *kubernetes.Clientset, namespace string, kubeClientConfig clientcmd.ClientConfig, timeout time.Duration) (*appsv1.StatefulSetList, error) { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - statefulSetList, err := client.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector, - }) - if err != nil { - if kerrors.IsForbidden(err) { - if namespace, err = getAccessibleNS(kubeClientConfig); err != nil { - return nil, err - } - return client.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: VirtualClusterSelector, - }) - } - return nil, err - } - return statefulSetList, nil -} - -func getAccessibleNS(kubeClientConfig clientcmd.ClientConfig) (string, error) { - // try the current namespace instead - namespace, _, err := kubeClientConfig.Namespace() - if err != nil { - return "", err - } else if namespace == "" { - namespace = "default" - } - return namespace, nil -} - -// GetPodStatus returns the pod status as a string -// Taken from https://github.com/kubernetes/kubernetes/pkg/printers/internalversion/printers.go -func GetPodStatus(pod *corev1.Pod) string { - reason := string(pod.Status.Phase) - if pod.Status.Reason != "" { - reason = pod.Status.Reason - } - initializing := false - for i := range pod.Status.InitContainerStatuses { - container := pod.Status.InitContainerStatuses[i] - switch { - case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0: - continue - case container.State.Terminated != nil: - // initialization is failed - if container.State.Terminated.Reason == "" { - if container.State.Terminated.Signal != 0 { - reason = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal) - } else { - reason = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode) - } - } else { - reason = "Init:" + container.State.Terminated.Reason - } - initializing = true - case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing": - reason = "Init:" + container.State.Waiting.Reason - initializing = true - default: - reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers)) - initializing = true - } - break - } - if !initializing { - hasRunning := false - for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- { - container := pod.Status.ContainerStatuses[i] - if container.State.Waiting != nil && container.State.Waiting.Reason != "" { - reason = container.State.Waiting.Reason - } else if container.State.Terminated != nil && container.State.Terminated.Reason != "" { - reason = container.State.Terminated.Reason - } else if container.State.Terminated != nil && container.State.Terminated.Reason == "" { - if container.State.Terminated.Signal != 0 { - reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal) - } else { - reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode) - } - } else if container.Ready && container.State.Running != nil { - hasRunning = true - } - } - // change pod status back to "Running" if there is at least one container still reporting as "Running" status - if reason == "Completed" && hasRunning { - reason = "Running" - } - } - if pod.DeletionTimestamp != nil && pod.Status.Reason == "NodeLost" { - reason = "Unknown" - } else if pod.DeletionTimestamp != nil { - reason = "Terminating" - } - return reason -} diff --git a/pkg/run/session/get.go b/pkg/run/session/get.go deleted file mode 100644 index 07b8a8bb3e..0000000000 --- a/pkg/run/session/get.go +++ /dev/null @@ -1,47 +0,0 @@ -package session - -import ( - "context" - "fmt" - "strings" - - "github.com/weaveworks/weave-gitops/core/server/types" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func Get(kubeClient client.Client, name, namespace string) (*InternalSession, error) { - var result *InternalSession - - statefulSet := appsv1.StatefulSet{} - if err := kubeClient.Get(context.Background(), client.ObjectKey{ - Namespace: namespace, - Name: name, - }, &statefulSet); err != nil { - if errors.IsNotFound(err) { - return nil, fmt.Errorf("session %s/%s not found", namespace, name) - } - - return nil, err - } - - labels := statefulSet.GetLabels() - if labels != nil { - if labels[types.AppLabel] != "vcluster" || labels[types.PartOfLabel] != "gitops-run" { - return nil, fmt.Errorf("%s/%s is an invalid GitOps Run session", namespace, name) - } - } - - annotations := statefulSet.GetAnnotations() - result = &InternalSession{ - SessionName: statefulSet.Name, - SessionNamespace: statefulSet.Namespace, - Command: annotations["run.weave.works/command"], - CliVersion: annotations["run.weave.works/cli-version"], - PortForward: strings.Split(annotations["run.weave.works/port-forward"], ","), - Namespace: annotations["run.weave.works/namespace"], - } - - return result, nil -} diff --git a/pkg/run/session/get_test.go b/pkg/run/session/get_test.go deleted file mode 100644 index 5f2e14ce65..0000000000 --- a/pkg/run/session/get_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package session - -import ( - "context" - "testing" - - . "github.com/onsi/gomega" - - v1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type mockGet struct { - client.Client -} - -var _ client.Client = &mockGet{} - -func (m *mockGet) Get(_ context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error { - switch obj := obj.(type) { - case *v1.StatefulSet: - *obj = v1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - Annotations: map[string]string{ - "run.weave.works/command": "command", - "run.weave.works/cli-version": "cli-version", - "run.weave.works/port-forward": "9999,1111", - "run.weave.works/namespace": "flux-system", - }, - }, - } - } - - return nil -} - -func TestGet(t *testing.T) { - g := NewGomegaWithT(t) - is, err := Get(&mockGet{}, "name", "namespace") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(is).ToNot(BeNil()) - g.Expect(is.SessionName).To(Equal("name")) - g.Expect(is.SessionNamespace).To(Equal("namespace")) - g.Expect(is.PortForward).To(Equal([]string{"9999", "1111"})) - g.Expect(is.Command).To(Equal("command")) - g.Expect(is.CliVersion).To(Equal("cli-version")) - g.Expect(is.Namespace).To(Equal("flux-system")) -} diff --git a/pkg/run/session/kubeconfig/kubeconfig.go b/pkg/run/session/kubeconfig/kubeconfig.go deleted file mode 100644 index d595d4f215..0000000000 --- a/pkg/run/session/kubeconfig/kubeconfig.go +++ /dev/null @@ -1,31 +0,0 @@ -package kubeconfig - -import ( - "context" - "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" -) - -const ( - DefaultSecretPrefix = "vc-" - KubeconfigSecretKey = "config" -) - -func ReadKubeConfig(ctx context.Context, client *kubernetes.Clientset, suffix, namespace string) (*api.Config, error) { - secret, err := client.CoreV1().Secrets(namespace).Get(ctx, GetDefaultSecretName(suffix), metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("could not Get the %s secret in order to read kubeconfig: %v", GetDefaultSecretName(suffix), err) - } - config, found := secret.Data[KubeconfigSecretKey] - if !found { - return nil, fmt.Errorf("could not find the kube config (%s key) in the %s secret", KubeconfigSecretKey, GetDefaultSecretName(suffix)) - } - return clientcmd.Load(config) -} - -func GetDefaultSecretName(suffix string) string { - return DefaultSecretPrefix + suffix -} diff --git a/pkg/run/session/list.go b/pkg/run/session/list.go deleted file mode 100644 index c5641323af..0000000000 --- a/pkg/run/session/list.go +++ /dev/null @@ -1,40 +0,0 @@ -package session - -import ( - "context" - "strings" - - "github.com/weaveworks/weave-gitops/core/server/types" - appsv1 "k8s.io/api/apps/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func List(kubeClient client.Client, targetNamespace string) ([]*InternalSession, error) { - var result []*InternalSession - - statefulSets := appsv1.StatefulSetList{} - if err := kubeClient.List(context.Background(), &statefulSets, - client.InNamespace(targetNamespace), - client.MatchingLabels(map[string]string{ - types.AppLabel: "vcluster", - types.PartOfLabel: "gitops-run", - }), - ); err != nil { - return nil, err - } - - for _, s := range statefulSets.Items { - annotations := s.GetAnnotations() - - result = append(result, &InternalSession{ - SessionName: s.Name, - SessionNamespace: s.Namespace, - Command: annotations["run.weave.works/command"], - CliVersion: annotations["run.weave.works/cli-version"], - PortForward: strings.Split(annotations["run.weave.works/port-forward"], ","), - Namespace: annotations["run.weave.works/namespace"], - }) - } - - return result, nil -} diff --git a/pkg/run/session/list_test.go b/pkg/run/session/list_test.go deleted file mode 100644 index 87b83bc704..0000000000 --- a/pkg/run/session/list_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package session - -import ( - "context" - "testing" - - . "github.com/onsi/gomega" - - v1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type mockList struct { - client.Client -} - -var _ client.Client = &mockList{} - -func (m *mockList) List(_ context.Context, list client.ObjectList, _ ...client.ListOption) error { - switch list := list.(type) { - case *v1.StatefulSetList: - *list = v1.StatefulSetList{ - Items: []v1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - Annotations: map[string]string{ - "run.weave.works/command": "command", - "run.weave.works/cli-version": "cli-version", - "run.weave.works/port-forward": "9999,1111", - "run.weave.works/namespace": "flux-system", - }, - }, - }, - }, - } - } - - return nil -} - -func TestList(t *testing.T) { - g := NewGomegaWithT(t) - list, err := List(&mockList{}, "namespace") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(list).ToNot(BeNil()) - g.Expect(list).To(HaveLen(1)) - g.Expect(list[0].SessionName).To(Equal("name")) - g.Expect(list[0].SessionNamespace).To(Equal("namespace")) - g.Expect(list[0].PortForward).To(Equal([]string{"9999", "1111"})) - g.Expect(list[0].Command).To(Equal("command")) - g.Expect(list[0].CliVersion).To(Equal("cli-version")) - g.Expect(list[0].Namespace).To(Equal("flux-system")) -} diff --git a/pkg/run/session/localkubernetes/configure.go b/pkg/run/session/localkubernetes/configure.go deleted file mode 100644 index 3297aaed2e..0000000000 --- a/pkg/run/session/localkubernetes/configure.go +++ /dev/null @@ -1,412 +0,0 @@ -package localkubernetes - -import ( - "context" - "fmt" - "net/url" - "os" - "os/exec" - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - log "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/session/find" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" -) - -func (c ClusterType) LocalKubernetes() bool { - return c == ClusterTypeDockerDesktop || - c == ClusterTypeRancherDesktop || - c == ClusterTypeKIND || - c == ClusterTypeMinikube || - c == ClusterTypeK3D -} - -func ExposeLocal(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, localPort int, log log.Logger) (string, error) { - // Timeout to wait for connection before falling back to port-forwarding - timeout := time.Second * 30 - clusterType := DetectClusterType(rawConfig) - switch clusterType { - case ClusterTypeDockerDesktop: - return directConnection(vRawConfig, service, timeout) - case ClusterTypeRancherDesktop: - return directConnection(vRawConfig, service, timeout) - case ClusterTypeKIND: - return kindProxy(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, log) - case ClusterTypeMinikube: - return minikubeProxy(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, log) - case ClusterTypeK3D: - return k3dProxy(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, log) - } - - return "", nil -} - -func k3dProxy(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, localPort int, timeout time.Duration, log log.Logger) (string, error) { - if len(service.Spec.Ports) != 1 { - return "", fmt.Errorf("service has %d ports (expected 1 port)", len(service.Spec.Ports)) - } - - // see if we already have a proxy container running - server, err := getServerFromExistingProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, log) - if err != nil { - return "", err - } else if server != "" { - return server, nil - } - - k3dName := strings.TrimPrefix(rawConfig.CurrentContext, "k3d-") - return createProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, "k3d-"+k3dName+"-server-0", "k3d-"+k3dName, log) -} - -func minikubeProxy(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, localPort int, timeout time.Duration, log log.Logger) (string, error) { - if len(service.Spec.Ports) != 1 { - return "", fmt.Errorf("service has %d ports (expected 1 port)", len(service.Spec.Ports)) - } - - // check if docker driver or vm - minikubeName := rawConfig.CurrentContext - if containerExists(minikubeName) { - // see if we already have a proxy container running - server, err := getServerFromExistingProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, log) - if err != nil { - return "", err - } else if server != "" { - return server, nil - } - - // create proxy container if missing - return createProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, minikubeName, minikubeName, log) - } - - // in case other type of driver (e.g. VM on linux) is used - // check if the service is reacheable directly via the minikube IP - c := rawConfig.Contexts[rawConfig.CurrentContext] - if c != nil { - s := rawConfig.Clusters[c.Cluster] - if s != nil { - u, err := url.Parse(s.Server) - if err == nil { - splitted := strings.Split(u.Host, ":") - server := fmt.Sprintf("https://%s:%v", splitted[0], service.Spec.Ports[0].NodePort) - - // workaround for the fact that vcluster certificate is not made valid for the node IPs - // but avoid modifying the passed config before the connection is tested - testvConfig := vRawConfig.DeepCopy() - for k := range testvConfig.Clusters { - testvConfig.Clusters[k].CertificateAuthorityData = nil - testvConfig.Clusters[k].InsecureSkipTLSVerify = true - } - - // test local connection - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, timeout, func() (bool, error) { - err = testConnectionWithServer(testvConfig, server) - if err != nil { - return false, nil - } - - return true, nil - }) - if waitErr != nil { - return "", fmt.Errorf("test connection: %v %v", waitErr, err) - } - - // now it's safe to modify the vRawConfig struct that was passed in as a pointer - for k := range vRawConfig.Clusters { - vRawConfig.Clusters[k].CertificateAuthorityData = nil - vRawConfig.Clusters[k].InsecureSkipTLSVerify = true - } - - return server, nil - } - } - } - - return "", nil -} - -func CleanupBackgroundProxy(proxyName string, log log.Logger) error { - // check if background proxy container already exists - if containerExists(proxyName) { - // remove background proxy container - cmd := exec.Command( - "docker", - "container", - "rm", - proxyName, - "-f", - ) - log.Actionf("Stopping background proxy...") - _, _ = cmd.Output() - } - return nil -} - -func cleanupProxy(vClusterName, vClusterNamespace string, rawConfig *clientcmdapi.Config, log log.Logger) { - // construct proxy name - proxyName := find.VClusterContextName(vClusterName, vClusterNamespace, rawConfig.CurrentContext) - - // check if proxy container already exists - cmd := exec.Command( - "docker", - "stop", - proxyName, - ) - log.Actionf("Stopping docker proxy...") - _, _ = cmd.Output() -} - -func kindProxy(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, localPort int, timeout time.Duration, log log.Logger) (string, error) { - if len(service.Spec.Ports) != 1 { - return "", fmt.Errorf("service has %d ports (expected 1 port)", len(service.Spec.Ports)) - } - - // see if we already have a proxy container running - server, err := getServerFromExistingProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, log) - if err != nil { - return "", err - } else if server != "" { - return server, nil - } - - // name is prefixed with kind- and suffixed with -control-plane - controlPlane := strings.TrimPrefix(rawConfig.CurrentContext, "kind-") + "-control-plane" - return createProxyContainer(vClusterName, vClusterNamespace, rawConfig, vRawConfig, service, localPort, timeout, controlPlane, "kind", log) -} - -func directConnection(vRawConfig *clientcmdapi.Config, service *corev1.Service, timeout time.Duration) (string, error) { - if len(service.Spec.Ports) != 1 { - return "", fmt.Errorf("service has %d ports (expected 1 port)", len(service.Spec.Ports)) - } - - server := fmt.Sprintf("https://127.0.0.1:%v", service.Spec.Ports[0].NodePort) - var err error - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, timeout, func() (bool, error) { - err = testConnectionWithServer(vRawConfig, server) - if err != nil { - return false, nil - } - - return true, nil - }) - if waitErr != nil { - return "", fmt.Errorf("test connection: %v %v", waitErr, err) - } - - return server, nil -} - -func createProxyContainer(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, localPort int, timeout time.Duration, backendHost, network string, log log.Logger) (string, error) { - // construct proxy name - proxyName := find.VClusterContextName(vClusterName, vClusterNamespace, rawConfig.CurrentContext) - - // in general, we need to run this statement to expose the correct port for this - // docker run -d -p LOCAL_PORT:NODE_PORT --rm -e "BACKEND_HOST=NAME-control-plane" -e "BACKEND_PORT=NODE_PORT" --network=NETWORK ghcr.io/loft-sh/docker-tcp-proxy - cmd := exec.Command( - "docker", - "run", - "-d", - "-p", - fmt.Sprintf("%v:%v", localPort, service.Spec.Ports[0].NodePort), - "--rm", - fmt.Sprintf("--name=%s", proxyName), - "-e", - fmt.Sprintf("BACKEND_HOST=%s", backendHost), - "-e", - fmt.Sprintf("BACKEND_PORT=%v", service.Spec.Ports[0].NodePort), - fmt.Sprintf("--network=%s", network), - "ghcr.io/loft-sh/docker-tcp-proxy", - ) - log.Actionf("Starting proxy container...") - out, err := cmd.Output() - if err != nil { - return "", errors.Errorf("error starting kind proxy: %s %v", string(out), err) - } - - server := fmt.Sprintf("https://127.0.0.1:%v", localPort) - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, timeout, func() (bool, error) { - err = testConnectionWithServer(vRawConfig, server) - if err != nil { - return false, nil - } - - return true, nil - }) - if waitErr != nil { - return "", fmt.Errorf("test connection: %v %v", waitErr, err) - } - - return server, nil -} - -func CreateBackgroundProxyContainer(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, localPort int, log log.Logger) (string, error) { - // write kube config to buffer - physicalCluster, err := clientcmd.Write(*rawConfig) - if err != nil { - return "", nil - } - // write a temporary kube file - tempFile, err := os.CreateTemp("", "") - if err != nil { - return "", errors.Wrap(err, "create temp file") - } - defer func(name string) { - _ = os.Remove(name) - }(tempFile.Name()) - _, err = tempFile.Write(physicalCluster) - if err != nil { - return "", errors.Wrap(err, "write kube config to temp file") - } - err = tempFile.Close() - if err != nil { - return "", errors.Wrap(err, "close temp file") - } - kubeConfigPath := tempFile.Name() - - // construct proxy name - proxyName := find.VClusterConnectBackgroundProxyName(vClusterName, vClusterNamespace, rawConfig.CurrentContext) - - // check if the background proxy container for this vcluster is running and then remove it. - _ = CleanupBackgroundProxy(proxyName, log) - - // docker run -d --network=host -v /root/.kube/config:/root/.kube/config loftsh/vcluster-cli vcluster connect vcluster -n vcluster --local-port 13300 - cmd := exec.Command( - "docker", - "run", - "-d", - "-v", - fmt.Sprintf("%v:%v", kubeConfigPath, "/root/.kube/config"), - fmt.Sprintf("--name=%s", proxyName), - fmt.Sprintf("--network=%s", "host"), - "loftsh/vcluster-cli", - "vcluster", - "connect", - vClusterName, - "--local-port", - strconv.Itoa(localPort), - "-n", - vClusterNamespace, - ) - log.Actionf("Starting background proxy container...") - out, err := cmd.Output() - if err != nil { - return "", errors.Errorf("error starting background proxy : %s %v", string(out), err) - } - server := fmt.Sprintf("https://127.0.0.1:%v", localPort) - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, time.Second*60, func() (bool, error) { - err = testConnectionWithServer(vRawConfig, server) - if err != nil { - return false, nil - } - return true, nil - }) - if waitErr != nil { - return "", fmt.Errorf("test connection: %v %v", waitErr, err) - } - return server, nil -} - -func IsDockerInstalledAndUpAndRunning() bool { - cmd := exec.Command( - "docker", - "ps", - ) - _, err := cmd.Output() - return err == nil -} - -func testConnectionWithServer(vRawConfig *clientcmdapi.Config, server string) error { - vRawConfig = vRawConfig.DeepCopy() - for k := range vRawConfig.Clusters { - vRawConfig.Clusters[k].Server = server - } - - restConfig, err := clientcmd.NewDefaultClientConfig(*vRawConfig, &clientcmd.ConfigOverrides{}).ClientConfig() - if err != nil { - return err - } - - kubeClient, err := kubernetes.NewForConfig(restConfig) - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) - defer cancel() - - _, err = kubeClient.CoreV1().Namespaces().Get(ctx, "default", metav1.GetOptions{}) - if err != nil { - return errors.Wrap(err, "retrieve default namespace") - } - - return nil -} - -func getServerFromExistingProxyContainer(vClusterName, vClusterNamespace string, rawConfig, vRawConfig *clientcmdapi.Config, service *corev1.Service, log log.Logger) (string, error) { - // construct proxy name - proxyName := find.VClusterContextName(vClusterName, vClusterNamespace, rawConfig.CurrentContext) - - // check if proxy container already exists - cmd := exec.Command( - "docker", - "inspect", - proxyName, - "-f", - fmt.Sprintf("{{ index (index (index .HostConfig.PortBindings \"%v/tcp\") 0) \"HostPort\" }}", service.Spec.Ports[0].NodePort), - ) - out, err := cmd.Output() - if err == nil { - localPort, err := strconv.Atoi(strings.TrimSpace(string(out))) - if err == nil && localPort != 0 { - server := fmt.Sprintf("https://127.0.0.1:%v", localPort) - - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - waitErr := wait.PollImmediate(time.Second, time.Second*5, func() (bool, error) { - err = testConnectionWithServer(vRawConfig, server) - if err != nil { - return false, nil - } - - return true, nil - }) - if waitErr != nil { - // return err here as waitErr is only timed out - return "", errors.Wrap(err, "test connection") - } - - return server, nil - } - } else { - log.Failuref("Error running docker inspect with go template: %v", err) - } - - if containerExists(proxyName) { - cleanupProxy(vClusterName, vClusterNamespace, rawConfig, log) - } - - return "", nil -} - -func containerExists(containerName string) bool { - cmd := exec.Command( - "docker", - "inspect", - "--type=container", - containerName, - ) - _, err := cmd.Output() - return err == nil -} diff --git a/pkg/run/session/localkubernetes/localkubernetes.go b/pkg/run/session/localkubernetes/localkubernetes.go deleted file mode 100644 index 408d6b7b25..0000000000 --- a/pkg/run/session/localkubernetes/localkubernetes.go +++ /dev/null @@ -1,90 +0,0 @@ -package localkubernetes - -import ( - "github.com/mitchellh/go-homedir" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "path/filepath" - "strings" -) - -type ClusterType string - -func (c ClusterType) String() string { return string(c) } - -const ( - ClusterTypeUnknown ClusterType = "unknown" - ClusterTypeVCluster ClusterType = "vcluster" - ClusterTypeMinikube ClusterType = "minikube" - ClusterTypeDockerDesktop ClusterType = "docker-desktop" - ClusterTypeMicroK8s ClusterType = "microk8s" - ClusterTypeCRC ClusterType = "crc" - ClusterTypeKrucible ClusterType = "krucible" - ClusterTypeKIND ClusterType = "kind" - ClusterTypeK3D ClusterType = "k3d" - ClusterTypeRancherDesktop ClusterType = "rancher-desktop" - ClusterTypeColima ClusterType = "colima" -) - -// DetectClusterType detects the k8s distro locally. -// Mostly taken from github.com/tilt-dev/clusterid, with some adjustments for vcluster -func DetectClusterType(config *clientcmdapi.Config) ClusterType { - if config == nil || config.Contexts == nil || config.Clusters == nil { - return ClusterTypeUnknown - } - - c := config.Contexts[config.CurrentContext] - if c == nil { - return ClusterTypeUnknown - } - - cl := config.Clusters[c.Cluster] - if cl == nil { - return ClusterTypeUnknown - } - - cn := c.Cluster - if strings.HasPrefix(cn, string(ClusterTypeVCluster)+"_") { - return ClusterTypeVCluster - } else if strings.HasPrefix(cn, string(ClusterTypeMinikube)) { - return ClusterTypeMinikube - } else if strings.HasPrefix(cn, "docker-for-desktop-cluster") || strings.HasPrefix(cn, "docker-desktop") { - return ClusterTypeDockerDesktop - } else if cn == "kind" { - return ClusterTypeKIND - } else if strings.HasPrefix(cn, "kind-") { - // As of KinD 0.6.0, KinD uses a context name prefix - // https://github.com/kubernetes-sigs/kind/issues/1060 - return ClusterTypeKIND - } else if strings.HasPrefix(cn, "microk8s-cluster") { - return ClusterTypeMicroK8s - } else if strings.HasPrefix(cn, "api-crc-testing") { - return ClusterTypeCRC - } else if strings.HasPrefix(cn, "krucible-") { - return ClusterTypeKrucible - } else if strings.HasPrefix(cn, "k3d-") { - return ClusterTypeK3D - } else if strings.HasPrefix(cn, "rancher-desktop") { - return ClusterTypeRancherDesktop - } else if strings.HasPrefix(cn, "colima") { - return ClusterTypeColima - } - - loc := c.LocationOfOrigin - homedir, err := homedir.Dir() - if err != nil { - return ClusterTypeUnknown - } - - k3dDir := filepath.Join(homedir, ".config", "k3d") - if strings.HasPrefix(loc, k3dDir+string(filepath.Separator)) { - return ClusterTypeK3D - } - - minikubeDir := filepath.Join(homedir, ".minikube") - if cl != nil && cl.CertificateAuthority != "" && - strings.HasPrefix(cl.CertificateAuthority, minikubeDir+string(filepath.Separator)) { - return ClusterTypeMinikube - } - - return ClusterTypeUnknown -} diff --git a/pkg/run/session/portforward/portforward.go b/pkg/run/session/portforward/portforward.go deleted file mode 100644 index f1df722640..0000000000 --- a/pkg/run/session/portforward/portforward.go +++ /dev/null @@ -1,459 +0,0 @@ -/* -Copyright 2015 The Kubernetes 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 portforward - -import ( - "errors" - "fmt" - "io" - "net" - "net/http" - "sort" - "strconv" - "strings" - "sync" - - "go.uber.org/atomic" - - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/httpstream" -) - -// PortForwardProtocolV1Name is the subprotocol used for port forwarding. -// TODO move to API machinery and re-unify with kubelet/server/portfoward -const PortForwardProtocolV1Name = "portforward.k8s.io" - -// PortForwarder knows how to listen for local connections and forward them to -// a remote pod via an upgraded HTTP request. -type PortForwarder struct { - addresses []listenAddress - ports []ForwardedPort - stopChan <-chan struct{} - - errChan chan<- error - - dialer httpstream.Dialer - streamConn httpstream.Connection - listeners []io.Closer - Ready chan struct{} - requestIDLock sync.Mutex - requestID int - out io.Writer - errOut io.Writer - - numConnections atomic.Int64 -} - -// ForwardedPort contains a Local:Remote port pairing. -type ForwardedPort struct { - Local uint16 - Remote uint16 -} - -/* -valid port specifications: - -5000 -- forwards from localhost:5000 to pod:5000 - -8888:5000 -- forwards from localhost:8888 to pod:5000 - -0:5000 -:5000 - - selects a random available local port, - forwards from localhost: to pod:5000 -*/ -func parsePorts(ports []string) ([]ForwardedPort, error) { - var forwards []ForwardedPort - for _, portString := range ports { - parts := strings.Split(portString, ":") - var localString, remoteString string - if len(parts) == 1 { - localString = parts[0] - remoteString = parts[0] - } else if len(parts) == 2 { - localString = parts[0] - if localString == "" { - // support :5000 - localString = "0" - } - remoteString = parts[1] - } else { - return nil, fmt.Errorf("invalid port format '%s'", portString) - } - - localPort, err := strconv.ParseUint(localString, 10, 16) - if err != nil { - return nil, fmt.Errorf("error parsing local port '%s': %s", localString, err) - } - - remotePort, err := strconv.ParseUint(remoteString, 10, 16) - if err != nil { - return nil, fmt.Errorf("error parsing remote port '%s': %s", remoteString, err) - } - if remotePort == 0 { - return nil, fmt.Errorf("remote port must be > 0") - } - - forwards = append(forwards, ForwardedPort{uint16(localPort), uint16(remotePort)}) - } - - return forwards, nil -} - -type listenAddress struct { - address string - protocol string - failureMode string -} - -func parseAddresses(addressesToParse []string) ([]listenAddress, error) { - var addresses []listenAddress - parsed := make(map[string]listenAddress) - for _, address := range addressesToParse { - if address == "localhost" { - if _, exists := parsed["127.0.0.1"]; !exists { - ip := listenAddress{address: "127.0.0.1", protocol: "tcp4", failureMode: "all"} - parsed[ip.address] = ip - } - if _, exists := parsed["::1"]; !exists { - ip := listenAddress{address: "::1", protocol: "tcp6", failureMode: "all"} - parsed[ip.address] = ip - } - } else if net.ParseIP(address).To4() != nil { - parsed[address] = listenAddress{address: address, protocol: "tcp4", failureMode: "any"} - } else if net.ParseIP(address) != nil { - parsed[address] = listenAddress{address: address, protocol: "tcp6", failureMode: "any"} - } else { - return nil, fmt.Errorf("%s is not a valid IP", address) - } - } - addresses = make([]listenAddress, len(parsed)) - id := 0 - for _, v := range parsed { - addresses[id] = v - id++ - } - // Sort addresses before returning to get a stable order - sort.Slice(addresses, func(i, j int) bool { return addresses[i].address < addresses[j].address }) - - return addresses, nil -} - -// NewOnAddresses creates a new PortForwarder with custom listen addresses. -func NewOnAddresses(dialer httpstream.Dialer, addresses, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, errChan chan<- error, out, errOut io.Writer) (*PortForwarder, error) { - if len(addresses) == 0 { - return nil, errors.New("you must specify at least 1 address") - } - parsedAddresses, err := parseAddresses(addresses) - if err != nil { - return nil, err - } - if len(ports) == 0 { - return nil, errors.New("you must specify at least 1 port") - } - parsedPorts, err := parsePorts(ports) - if err != nil { - return nil, err - } - return &PortForwarder{ - dialer: dialer, - addresses: parsedAddresses, - ports: parsedPorts, - stopChan: stopChan, - Ready: readyChan, - out: out, - errChan: errChan, - errOut: errOut, - }, nil -} - -func (pf *PortForwarder) raiseError(err error) { - if pf.errChan != nil { - pf.errChan <- err - } -} - -func (pf *PortForwarder) NumConnections() int64 { - return pf.numConnections.Load() -} - -// ForwardPorts formats and executes a port forwarding request. The connection will remain -// open until stopChan is closed. -func (pf *PortForwarder) ForwardPorts() error { - defer pf.Close() - - var err error - pf.streamConn, _, err = pf.dialer.Dial(PortForwardProtocolV1Name) - if err != nil { - return fmt.Errorf("error upgrading connection: %s", err) - } - defer func(streamConn httpstream.Connection) { - _ = streamConn.Close() - }(pf.streamConn) - - return pf.forward() -} - -// forward dials the remote host specific in req, upgrades the request, starts -// listeners for each port specified in ports, and forwards local connections -// to the remote host via streams. -func (pf *PortForwarder) forward() error { - var err error - - listenSuccess := false - for i := range pf.ports { - port := &pf.ports[i] - err = pf.listenOnPort(port) - switch { - case err == nil: - listenSuccess = true - default: - if pf.errOut != nil { - _, _ = fmt.Fprintf(pf.errOut, "Unable to listen on port %d: %v\n", port.Local, err) - } - } - } - - if !listenSuccess { - return fmt.Errorf("unable to listen on any of the requested ports: %v", pf.ports) - } - - if pf.Ready != nil { - close(pf.Ready) - } - - // wait for interrupt or conn closure - select { - case <-pf.stopChan: - case <-pf.streamConn.CloseChan(): - pf.raiseError(errors.New("lost connection to pod")) - } - - return nil -} - -// listenOnPort delegates listener creation and waits for connections on requested bind addresses. -// An error is raised based on address groups (default and localhost) and their failure modes -func (pf *PortForwarder) listenOnPort(port *ForwardedPort) error { - var errorsSlice []error - failCounters := make(map[string]int, 2) - successCounters := make(map[string]int, 2) - for _, addr := range pf.addresses { - err := pf.listenOnPortAndAddress(port, addr.protocol, addr.address) - if err != nil { - errorsSlice = append(errorsSlice, err) - failCounters[addr.failureMode]++ - } else { - successCounters[addr.failureMode]++ - } - } - if successCounters["all"] == 0 && failCounters["all"] > 0 { - return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errorsSlice) - } - if failCounters["any"] > 0 { - return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errorsSlice) - } - return nil -} - -// listenOnPortAndAddress delegates listener creation and waits for new connections -// in the background f -func (pf *PortForwarder) listenOnPortAndAddress(port *ForwardedPort, protocol, address string) error { - listener, err := pf.getListener(protocol, address, port) - if err != nil { - return err - } - pf.listeners = append(pf.listeners, listener) - go pf.waitForConnection(listener, *port) - return nil -} - -// getListener creates a listener on the interface targeted by the given hostname on the given port with -// the given protocol. protocol is in net.Listen style which basically admits values like tcp, tcp4, tcp6 -func (pf *PortForwarder) getListener(protocol, hostname string, port *ForwardedPort) (net.Listener, error) { - listener, err := net.Listen(protocol, net.JoinHostPort(hostname, strconv.Itoa(int(port.Local)))) - if err != nil { - return nil, fmt.Errorf("unable to create listener: Error %s", err) - } - listenerAddress := listener.Addr().String() - host, localPort, _ := net.SplitHostPort(listenerAddress) - localPortUInt, err := strconv.ParseUint(localPort, 10, 16) - - if err != nil { - _, _ = fmt.Fprintf(pf.out, "Failed to forward from %s:%d -> %d\n", hostname, localPortUInt, port.Remote) - return nil, fmt.Errorf("error parsing local port: %s from %s (%s)", err, listenerAddress, host) - } - port.Local = uint16(localPortUInt) - if pf.out != nil { - _, _ = fmt.Fprintf(pf.out, "Forwarding from %s -> %d\n", net.JoinHostPort(hostname, strconv.Itoa(int(localPortUInt))), port.Remote) - } - - return listener, nil -} - -// waitForConnection waits for new connections to listener and handles them in -// the background. -func (pf *PortForwarder) waitForConnection(listener net.Listener, port ForwardedPort) { - for { - conn, err := listener.Accept() - if err != nil { - // TODO consider using something like https://github.com/hydrogen18/stoppableListener? - if !strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") { - pf.raiseError(fmt.Errorf("error accepting connection on port %d: %v", port.Local, err)) - } - return - } - go pf.handleConnection(conn, port) - } -} - -func (pf *PortForwarder) nextRequestID() int { - pf.requestIDLock.Lock() - defer pf.requestIDLock.Unlock() - id := pf.requestID - pf.requestID++ - return id -} - -// handleConnection copies data between the local connection and the stream to -// the remote server. -func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) { - defer func(conn net.Conn) { - _ = conn.Close() - }(conn) - - pf.numConnections.Inc() - defer pf.numConnections.Dec() - if pf.out != nil { - _, _ = fmt.Fprintf(pf.out, "Handling connection for %d\n", port.Local) - } - - requestID := pf.nextRequestID() - - // create error stream - headers := http.Header{} - headers.Set(v1.StreamType, v1.StreamTypeError) - headers.Set(v1.PortHeader, fmt.Sprintf("%d", port.Remote)) - headers.Set(v1.PortForwardRequestIDHeader, strconv.Itoa(requestID)) - errorStream, err := pf.streamConn.CreateStream(headers) - if err != nil { - pf.raiseError(fmt.Errorf("error creating error stream for port %d -> %d: %v", port.Local, port.Remote, err)) - return - } - // we're not writing to this stream - _ = errorStream.Close() - - errorChan := make(chan error) - go func() { - message, err := io.ReadAll(errorStream) - switch { - case err != nil: - errorChan <- fmt.Errorf("error reading from error stream for port %d -> %d: %v", port.Local, port.Remote, err) - case len(message) > 0: - errorChan <- fmt.Errorf("an error occurred forwarding %d -> %d: %v", port.Local, port.Remote, string(message)) - } - close(errorChan) - }() - - // create data stream - headers.Set(v1.StreamType, v1.StreamTypeData) - dataStream, err := pf.streamConn.CreateStream(headers) - if err != nil { - pf.raiseError(fmt.Errorf("error creating forwarding stream for port %d -> %d: %v", port.Local, port.Remote, err)) - return - } - - localError := make(chan struct{}) - remoteDone := make(chan struct{}) - - go func() { - // Copy from the remote side to the local port. - if _, err := io.Copy(conn, dataStream); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - if pf.errOut != nil { - _, _ = fmt.Fprintf(pf.errOut, "error copying from remote stream to local connection: %v\n", err) - } - } - - // inform the select below that the remote copy is done - close(remoteDone) - }() - - go func() { - // inform server we're not sending any more data after copy unblocks - defer func(dataStream httpstream.Stream) { - _ = dataStream.Close() - }(dataStream) - - // Copy from the local port to the remote side. - if _, err := io.Copy(dataStream, conn); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - if pf.errOut != nil { - _, _ = fmt.Fprintf(pf.errOut, "error copying from local connection to remote stream: %v\n", err) - } - - // break out of the select below without waiting for the other copy to finish - close(localError) - } - }() - - // wait for either a local->remote error or for copying from remote->local to finish - select { - case <-remoteDone: - case <-localError: - } - - // always expect something on errorChan (it may be nil) - err = <-errorChan - if err != nil { - // Fail for errors like container not running or No such container - if strings.Contains(err.Error(), "container") { - pf.raiseError(err) - } else if pf.errOut != nil { - _, _ = fmt.Fprintf(pf.errOut, "%v\n", err) - } - } -} - -// Close stops all listeners of PortForwarder. -func (pf *PortForwarder) Close() { - // stop all listeners - for _, l := range pf.listeners { - if err := l.Close(); err != nil { - if pf.errOut != nil { - _, _ = fmt.Fprintf(pf.errOut, "error closing listener: %v\n", err) - } - } - } -} - -// GetPorts will return the ports that were forwarded; this can be used to -// retrieve the locally-bound port in cases where the input was port 0. This -// function will signal an error if the Ready channel is nil or if the -// listeners are not ready yet; this function will succeed after the Ready -// channel has been closed. -func (pf *PortForwarder) GetPorts() ([]ForwardedPort, error) { - if pf.Ready == nil { - return nil, fmt.Errorf("no Ready channel provided") - } - select { - case <-pf.Ready: - return pf.ports, nil - default: - return nil, fmt.Errorf("listeners not ready") - } -} diff --git a/pkg/run/session/portforward/start.go b/pkg/run/session/portforward/start.go deleted file mode 100644 index b285462f9f..0000000000 --- a/pkg/run/session/portforward/start.go +++ /dev/null @@ -1,126 +0,0 @@ -package portforward - -import ( - "context" - "fmt" - "io" - "net/http" - "time" - - log "github.com/weaveworks/weave-gitops/pkg/logger" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/transport/spdy" -) - -func StartPortForwardingWithRestart(config *rest.Config, address, pod, namespace, localPort, remotePort string, interrupt chan struct{}, stdout, stderr io.Writer, log log.Logger) error { - kubeClient, err := kubernetes.NewForConfig(config) - if err != nil { - return err - } - - // restart port forwarding - stopChan, err := StartPortForwarding(config, kubeClient, address, pod, namespace, localPort, remotePort, stdout, stderr, log) - if err != nil { - return fmt.Errorf("error starting port forwarding: %v", err) - } - - for { - select { - case <-interrupt: - close(stopChan) - return nil - case <-stopChan: - log.Actionf("Restarting port forwarding") - - // wait for loft pod to start - //nolint:staticcheck // deprecated, tracking issue: https://github.com/weaveworks/weave-gitops/issues/3812 - err := wait.PollImmediate(time.Second, time.Minute*10, func() (done bool, err error) { - pod, err := kubeClient.CoreV1().Pods(namespace).Get(context.Background(), pod, metav1.GetOptions{}) - if err != nil { - return false, nil - } - for _, c := range pod.Status.Conditions { - if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { - return true, nil - } - } - return false, nil - }) - if err != nil { - log.Warningf("error waiting for ready vcluster pod: %v", err) - continue - } - - // restart port forwarding - stopChan, err = StartPortForwarding(config, kubeClient, address, pod, namespace, localPort, remotePort, stdout, stderr, log) - if err != nil { - log.Warningf("error starting port forwarding: %v", err) - continue - } - - log.Successf("Successfully restarted port forwarding") - } - } -} - -func StartPortForwarding(config *rest.Config, client kubernetes.Interface, address, pod, namespace, localPort, remotePort string, stdout, stderr io.Writer, log log.Logger) (chan struct{}, error) { - execRequest := client.CoreV1().RESTClient().Post(). - Resource("pods"). - Name(pod). - Namespace(namespace). - SubResource("portforward") - - t, upgrader, err := spdy.RoundTripperFor(config) - if err != nil { - return nil, err - } - - if address == "" { - address = "localhost" - } - - dialer := spdy.NewDialer(upgrader, &http.Client{Transport: t}, "POST", execRequest.URL()) - errChan := make(chan error) - readyChan := make(chan struct{}) - stopChan := make(chan struct{}) - forwarder, err := NewOnAddresses(dialer, []string{address}, []string{localPort + ":" + remotePort}, stopChan, readyChan, errChan, stdout, stderr) - if err != nil { - return nil, err - } - - go func() { - err := forwarder.ForwardPorts() - if err != nil { - errChan <- err - } - }() - - // wait till ready - select { - case err = <-errChan: - return nil, err - case <-readyChan: - case <-stopChan: - return nil, fmt.Errorf("stopped before ready") - } - - // start watcher - go func() { - for { - select { - case <-stopChan: - return - case err = <-errChan: - log.Actionf("error during port forwarder: %v", err) - close(stopChan) - return - } - } - }() - - return stopChan, nil -} diff --git a/pkg/run/session/remove.go b/pkg/run/session/remove.go deleted file mode 100644 index 67b1c1bfce..0000000000 --- a/pkg/run/session/remove.go +++ /dev/null @@ -1,88 +0,0 @@ -package session - -import ( - "context" - "fmt" - "time" - - helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" - "github.com/hashicorp/go-multierror" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type InternalSession struct { - SessionName string - SessionNamespace string - PortForward []string - CliVersion string - Command string - Namespace string -} - -func Remove(kubeClient client.Client, session *InternalSession) error { - var ( - helmRelease helmv2.HelmRelease - result error - ) - - if err := kubeClient.Get(context.Background(), - types.NamespacedName{ - Namespace: session.SessionNamespace, - Name: session.SessionName, - }, &helmRelease); err != nil { - result = multierror.Append(result, err) - } - - if err := kubeClient.Delete(context.Background(), &helmRelease); err != nil { - result = multierror.Append(result, err) - } - - if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { - instance := appsv1.StatefulSet{} - if err := kubeClient.Get( - context.Background(), - types.NamespacedName{ - Namespace: session.SessionNamespace, - Name: session.SessionName}, - &instance, - ); err != nil && apierrors.IsNotFound(err) { - return true, nil - } else if err != nil { - return false, err - } - - return false, nil - }); err != nil { - result = multierror.Append(result, err) - } - - if err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(_ context.Context) (bool, error) { - pvc := corev1.PersistentVolumeClaim{} - if err := kubeClient.Get( - context.Background(), - types.NamespacedName{ - Namespace: session.SessionNamespace, - Name: fmt.Sprintf("data-%s-0", session.SessionName), - }, - &pvc, - ); err != nil && apierrors.IsNotFound(err) { - return true, nil - } else if err != nil { - return false, err - } - - if err := kubeClient.Delete(context.Background(), &pvc); err != nil { - return false, err - } - return false, nil - }); err != nil { - result = multierror.Append(result, err) - } - - return result -} diff --git a/pkg/run/watch/install_dev_bucket_server.go b/pkg/run/watch/install_dev_bucket_server.go deleted file mode 100644 index 231b30a8e8..0000000000 --- a/pkg/run/watch/install_dev_bucket_server.go +++ /dev/null @@ -1,499 +0,0 @@ -package watch - -import ( - "context" - "fmt" - "k8s.io/apimachinery/pkg/runtime/schema" - "strconv" - "time" - - coretypes "github.com/weaveworks/weave-gitops/core/server/types" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - "github.com/weaveworks/weave-gitops/pkg/tls" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - // The variables below are to be set by flags passed to `go build`. - // Examples: -X run.DevBucketContainerImage=xxxxx - - DevBucketContainerImage string -) - -// InstallDevBucketServer installs the dev bucket server, open port forwarding, and returns a function that can be used to the port forwarding. -func InstallDevBucketServer( - ctx context.Context, - log logger.Logger, - kubeClient client.Client, - config *rest.Config, - httpPort, - httpsPort int32, - accessKey, - secretKey []byte) (func(), []byte, error) { - var ( - err error - devBucketAppLabels = map[string]string{ - coretypes.AppLabel: constants.RunDevBucketName, - } - ) - - // create namespace - devBucketNamespace := corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.GitOpsRunNamespace, - }, - } - - log.Actionf("Checking namespace %s ...", constants.GitOpsRunNamespace) - - err = kubeClient.Get(ctx, - client.ObjectKeyFromObject(&devBucketNamespace), - &devBucketNamespace) - - if err != nil && apierrors.IsNotFound(err) { - if err := kubeClient.Create(ctx, &devBucketNamespace); err != nil { - log.Failuref("Error creating namespace %s: %v", constants.GitOpsRunNamespace, err.Error()) - return nil, nil, err - } else { - log.Successf("Created namespace %s", constants.GitOpsRunNamespace) - } - } else if err == nil { - log.Successf("Namespace %s already existed", constants.GitOpsRunNamespace) - } - - // create service - devBucketService := corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: constants.GitOpsRunNamespace, - Labels: devBucketAppLabels, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - Ports: []corev1.ServicePort{ - { - Name: fmt.Sprintf("%s-http", constants.RunDevBucketName), - Port: httpPort, - }, - { - Name: fmt.Sprintf("%s-https", constants.RunDevBucketName), - Port: httpsPort, - }, - }, - Selector: devBucketAppLabels, - }, - } - - log.Actionf("Checking service %s/%s ...", constants.GitOpsRunNamespace, constants.RunDevBucketName) - - err = kubeClient.Get(ctx, - client.ObjectKeyFromObject(&devBucketService), - &devBucketService) - - if err != nil && apierrors.IsNotFound(err) { - if err := kubeClient.Create(ctx, &devBucketService); err != nil { - log.Failuref("Error creating service %s/%s: %v", constants.GitOpsRunNamespace, constants.RunDevBucketName, err.Error()) - return nil, nil, err - } else { - log.Successf("Created service %s/%s", constants.GitOpsRunNamespace, constants.RunDevBucketName) - } - } else if err == nil { - log.Successf("Service %s/%s already existed", constants.GitOpsRunNamespace, constants.RunDevBucketName) - } - - credentialsSecret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: constants.GitOpsRunNamespace, - Name: constants.RunDevBucketCredentials, - }, - Data: map[string][]byte{ - "accesskey": accessKey, - "secretkey": secretKey, - }, - } - if err := kubeClient.Create(ctx, &credentialsSecret); err != nil { - log.Failuref("Error creating credentials secret: %s", err.Error()) - return nil, nil, fmt.Errorf("failed creating credentials secret: %w", err) - } - - cert, err := tls.GenerateSelfSignedCertificate("localhost", fmt.Sprintf("%s.%s.svc.cluster.local", devBucketService.Name, devBucketService.Namespace)) - if err != nil { - err = fmt.Errorf("failed generating self-signed certificate for dev bucket server: %w", err) - log.Failuref(err.Error()) - - return nil, nil, err - } - - certsSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dev-bucket-server-certs", - Namespace: constants.GitOpsRunNamespace, - Labels: devBucketAppLabels, - }, - Data: map[string][]byte{ - "cert.pem": cert.Cert, - "cert.key": cert.Key, - }, - } - if err := kubeClient.Create(ctx, certsSecret); err != nil { - log.Failuref("Error creating Secret %s/%s: %v", certsSecret.Namespace, certsSecret.Name, err.Error()) - return nil, nil, err - } - - // create deployment - replicas := int32(1) - devBucketDeployment := appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: constants.GitOpsRunNamespace, - Labels: devBucketAppLabels, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: devBucketAppLabels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: devBucketAppLabels, - }, - Spec: corev1.PodSpec{ - Volumes: []corev1.Volume{{ - Name: "certs", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "dev-bucket-server-certs", - }, - }, - }}, - Containers: []corev1.Container{ - { - Name: constants.RunDevBucketName, - Image: DevBucketContainerImage, - ImagePullPolicy: corev1.PullIfNotPresent, - Env: []corev1.EnvVar{ - {Name: "MINIO_ROOT_USER", ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: credentialsSecret.Name}, - Key: "accesskey", - }, - }}, - {Name: "MINIO_ROOT_PASSWORD", ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: credentialsSecret.Name}, - Key: "secretkey", - }, - }}, - }, - Ports: []corev1.ContainerPort{ - { - ContainerPort: httpPort, - HostPort: httpPort, - }, - { - ContainerPort: httpsPort, - HostPort: httpsPort, - }, - }, - Args: []string{ - fmt.Sprintf("--http-port=%d", httpPort), - fmt.Sprintf("--https-port=%d", httpsPort), - "--cert-file=/tmp/certs/cert.pem", - "--key-file=/tmp/certs/cert.key", - }, - VolumeMounts: []corev1.VolumeMount{{ - Name: "certs", - MountPath: "/tmp/certs", - }}, - }, - }, - RestartPolicy: corev1.RestartPolicyAlways, - }, - }, - }, - } - - log.Actionf("Checking deployment %s/%s ...", constants.GitOpsRunNamespace, constants.RunDevBucketName) - - err = kubeClient.Get(ctx, - client.ObjectKeyFromObject(&devBucketDeployment), - &devBucketDeployment) - - if err != nil && apierrors.IsNotFound(err) { - if err := kubeClient.Create(ctx, &devBucketDeployment); err != nil { - log.Failuref("Error creating deployment %s/%s: %v", constants.GitOpsRunNamespace, constants.RunDevBucketName, err.Error()) - return nil, nil, err - } else { - log.Successf("Created deployment %s/%s", constants.GitOpsRunNamespace, constants.RunDevBucketName) - } - } else if err == nil { - log.Successf("Deployment %s/%s already existed", constants.GitOpsRunNamespace, constants.RunDevBucketName) - } - - log.Actionf("Waiting for deployment %s to be ready ...", constants.RunDevBucketName) - - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: 1 * time.Second, - Factor: 2, - Jitter: 1, - Steps: 10, - }, func() (done bool, err error) { - d := devBucketDeployment.DeepCopy() - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(d), d); err != nil { - return false, err - } - // Confirm the state we are observing is for the current generation - if d.Generation != d.Status.ObservedGeneration { - return false, nil - } - - if d.Status.ReadyReplicas == 1 { - return true, nil - } - - return false, nil - }); err != nil { - log.Failuref("Max retry exceeded waiting for deployment to be ready") - } - - specMap := &PortForwardSpec{ - Name: constants.RunDevBucketName, - Namespace: constants.GitOpsRunNamespace, - Kind: "service", - HostPort: strconv.Itoa(int(httpsPort)), - ContainerPort: strconv.Itoa(int(httpsPort)), - } - // get pod from specMap - namespacedName := types.NamespacedName{Namespace: specMap.Namespace, Name: specMap.Name} - - pod, err := run.GetPodFromResourceDescription(ctx, kubeClient, namespacedName, specMap.Kind, nil) - if err != nil { - log.Failuref("Error getting pod from specMap: %v", err) - } - - if pod != nil { - waitFwd := make(chan struct{}, 1) - readyChannel := make(chan struct{}) - cancelPortFwd := func() { - close(waitFwd) - } - - log.Actionf("Port forwarding to pod %s/%s ...", pod.Namespace, pod.Name) - - go func() { - if err := ForwardPort(log.L(), pod, config, specMap, waitFwd, readyChannel); err != nil { - log.Failuref("Error forwarding port: %v", err) - } - }() - <-readyChannel - - log.Successf("Port forwarding for %s is ready.", constants.RunDevBucketName) - - return cancelPortFwd, cert.Cert, nil - } - - return nil, nil, fmt.Errorf("pod not found") -} - -type resourceToDelete struct { - key types.NamespacedName - gvk schema.GroupVersionKind -} - -func devBucketCleanUpFunc(ctx context.Context, log logger.Logger, kubeClient client.Client) ([]resourceToDelete, error) { - // Rsources to delete: - // Service: constants.RunDevBucketName in the namespace constants.GitOpsRunNamespace - // Deployment: constants.RunDevBucketName in the namespace constants.GitOpsRunNamespace - // Secret: dev-bucket-server-certs in the namespace constants.GitOpsRunNamespace - // Secret: constants.RunDevBucketCredentials in the namespace constants.GitOpsRunNamespace - // Namespace: constants.GitOpsRunNamespace - - var allResources []resourceToDelete - - // delete deployment - devBucketDeployment := appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: constants.GitOpsRunNamespace, - }, - } - log.Actionf("Deleting deployment %s/%s ...", constants.GitOpsRunNamespace, constants.RunDevBucketName) - - if err := kubeClient.Delete(ctx, &devBucketDeployment); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting deployment %s/%s: %v", constants.GitOpsRunNamespace, constants.RunDevBucketName, err.Error()) - return nil, err - } - } - allResources = append(allResources, resourceToDelete{ - key: client.ObjectKeyFromObject(&devBucketDeployment), - gvk: devBucketDeployment.GroupVersionKind(), - }) - - // delete service - devBucketService := corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: constants.GitOpsRunNamespace, - }, - } - log.Actionf("Deleting service %s/%s ...", constants.GitOpsRunNamespace, constants.RunDevBucketName) - - if err := kubeClient.Delete(ctx, &devBucketService); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting service %s/%s: %v", constants.GitOpsRunNamespace, constants.RunDevBucketName, err.Error()) - return nil, err - } - } - allResources = append(allResources, resourceToDelete{ - key: client.ObjectKeyFromObject(&devBucketService), - gvk: devBucketService.GroupVersionKind(), - }) - - // delete secret - devBucketSecret := corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketCredentials, - Namespace: constants.GitOpsRunNamespace, - }, - } - log.Actionf("Deleting secret %s/%s ...", constants.GitOpsRunNamespace, constants.RunDevBucketCredentials) - - if err := kubeClient.Delete(ctx, &devBucketSecret); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting secret %s/%s: %v", constants.GitOpsRunNamespace, constants.RunDevBucketCredentials, err.Error()) - return nil, err - } - } - allResources = append(allResources, resourceToDelete{ - key: client.ObjectKeyFromObject(&devBucketSecret), - gvk: devBucketSecret.GroupVersionKind(), - }) - - // delete secret - devBucketServerCerts := corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "dev-bucket-server-certs", - Namespace: constants.GitOpsRunNamespace, - }, - } - log.Actionf("Deleting secret %s/%s ...", constants.GitOpsRunNamespace, "dev-bucket-server-certs") - - if err := kubeClient.Delete(ctx, &devBucketServerCerts); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting secret %s/%s: %v", constants.GitOpsRunNamespace, "dev-bucket-server-certs", err.Error()) - return nil, err - } - } - allResources = append(allResources, resourceToDelete{ - key: client.ObjectKeyFromObject(&devBucketServerCerts), - gvk: devBucketServerCerts.GroupVersionKind(), - }) - - // delete namespace - devBucketNamespace := corev1.Namespace{ - TypeMeta: metav1.TypeMeta{ - Kind: "Namespace", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: constants.GitOpsRunNamespace, - }, - } - log.Actionf("Deleting namespace %s ...", constants.GitOpsRunNamespace) - - if err := kubeClient.Delete(ctx, &devBucketNamespace); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting namespace %s: %v", constants.GitOpsRunNamespace, err.Error()) - return nil, err - } - } - - allResources = append(allResources, resourceToDelete{ - key: client.ObjectKeyFromObject(&devBucketNamespace), - gvk: devBucketNamespace.GroupVersionKind(), - }) - - return allResources, nil -} - -// UninstallDevBucketServer deletes the dev-bucket namespace. -func UninstallDevBucketServer(ctx context.Context, log logger.Logger, kubeClient client.Client) error { - resources, err := devBucketCleanUpFunc(ctx, log, kubeClient) - if err != nil { - return err - } - - log.Actionf("Waiting for resources to be terminated ...") - - // The purpose of this code is to wait for a list of Kubernetes resources to be deleted from a namespace, - // using an exponential backoff strategy to avoid overloading the Kubernetes API server with requests. - // - // The wait.ExponentialBackoff function is called with a wait.Backoff struct, defining the exponential backoff settings. - // The function provided as the second argument to wait.ExponentialBackoff checks the status of the Kubernetes resources in the resources slice. - // For each resource in the resources slice, the code attempts to retrieve the resource using kubeClient.Get. - // - If the resource is not found (i.e., apierrors.IsNotFound(err) returns true), the loop continues checking the next resource. - // - If the resource is found (i.e., there is no error), the function returns false, nil, indicating that the operation is not yet done. - // The wait.ExponentialBackoff function will retry the operation based on the backoff settings. - // - If an error other than "not found" occurs, the function returns false, err. - // The wait.ExponentialBackoff function stops retrying immediately and returns the error. - // - If all resources are checked and not found, the function returns true, nil, - // indicating that the operation is done, and no errors occurred. - // - If the maximum number of retries (backoff.Steps) is reached or the backoff duration is capped, - // and the resources are not yet deleted, the wait.ExponentialBackoff function returns ErrWaitTimeout. - // In this case, the log message "Max retry exceeded waiting for resources to be deleted" will be printed. - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: 1 * time.Second, - Factor: 2, - Jitter: 1, - Steps: 10, - }, func() (done bool, err error) { - for _, resource := range resources { - u := &unstructured.Unstructured{} - // u.SetGroupVersionKind(resource.gvk) - u.SetKind(resource.gvk.Kind) - u.SetAPIVersion(resource.gvk.GroupVersion().String()) - if err := kubeClient.Get(ctx, resource.key, u); err != nil { - if apierrors.IsNotFound(err) { - continue - } else { - return false, err - } - } - return false, nil - } - return true, nil - }); err != nil { - log.Failuref("Max retry exceeded waiting for resources to be deleted: %v", err.Error()) - } - - log.Successf("Resources terminated") - - return nil -} diff --git a/pkg/run/watch/setup_bucket_source.go b/pkg/run/watch/setup_bucket_source.go deleted file mode 100644 index 2707ebfab9..0000000000 --- a/pkg/run/watch/setup_bucket_source.go +++ /dev/null @@ -1,129 +0,0 @@ -package watch - -import ( - "context" - "fmt" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" - "time" - - "github.com/fluxcd/pkg/apis/meta" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func createBucketAndSecretObjects(params SetupRunObjectParams) (corev1.Secret, sourcev1b2.Bucket) { - // create a secret - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketCredentials, - Namespace: params.Namespace, - }, - Data: map[string][]byte{ - "accesskey": params.AccessKey, - "secretkey": params.SecretKey, - }, - Type: "Opaque", - } - source := sourcev1b2.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: params.Namespace, - Annotations: map[string]string{ - "metadata.weave.works/description": "This is a temporary Bucket created by GitOps Run. This will be cleaned up when this instance of GitOps Run is ended.", - "metadata.weave.works/run-id": params.SessionName, - "metadata.weave.works/username": params.Username, - }, - }, - Spec: sourcev1b2.BucketSpec{ - Interval: metav1.Duration{Duration: 30 * 24 * time.Hour}, // 30 days - Provider: "generic", - BucketName: constants.RunDevBucketName, - Endpoint: fmt.Sprintf("%s.%s.svc.cluster.local:%d", constants.RunDevBucketName, constants.GitOpsRunNamespace, params.DevBucketPort), - Insecure: true, - Timeout: &metav1.Duration{Duration: params.Timeout}, - SecretRef: &meta.LocalObjectReference{Name: constants.RunDevBucketCredentials}, - }, - } - - return secret, source -} - -func reconcileBucketAndSecretObjects(ctx context.Context, log logger.Logger, kubeClient client.Client, secret corev1.Secret, source sourcev1b2.Bucket) error { - // create secret - log.Actionf("Checking secret %s ...", secret.Name) - - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(&secret), &secret); err != nil { - if !apierrors.IsNotFound(err) { - return fmt.Errorf("failed fetching secret %s/%s: %w", secret.Namespace, secret.Name, err) - } - - if err := kubeClient.Create(ctx, &secret); err != nil { - return fmt.Errorf("couldn't create secret %s: %v", secret.Name, err.Error()) - } - - log.Successf("Created secret %s", secret.Name) - } - - log.Successf("Secret %s already existed", secret.Name) - - // create source - log.Actionf("Checking bucket source %s ...", source.Name) - - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(&source), &source); err != nil { - if !apierrors.IsNotFound(err) { - return fmt.Errorf("failed fetching bucket source %s/%s: %w", source.Namespace, source.Name, err) - } - - if err := kubeClient.Create(ctx, &source); err != nil { - return fmt.Errorf("couldn't create source %s: %v", source.Name, err.Error()) - } - - log.Successf("Created source %s", source.Name) - } - - log.Successf("Source %s already existed", source.Name) - - return nil -} - -func cleanupBucketAndSecretObjects(ctx context.Context, log logger.Logger, kubeClient client.Client, namespace string) { - // delete secret - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketCredentials, - Namespace: namespace, - }, - } - - log.Actionf("Deleting secret %s ...", secret.Name) - - if err := kubeClient.Delete(ctx, &secret); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting secret %s: %v", secret.Name, err.Error()) - } - } else { - log.Successf("Deleted secret %s", secret.Name) - } - - // delete source - source := sourcev1b2.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, - } - - log.Actionf("Deleting source %s ...", source.Name) - - if err := kubeClient.Delete(ctx, &source); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting source %s: %v", source.Name, err.Error()) - } - } else { - log.Successf("Deleted source %s", source.Name) - } -} diff --git a/pkg/run/watch/setup_dev_helm.go b/pkg/run/watch/setup_dev_helm.go deleted file mode 100644 index a5425b156c..0000000000 --- a/pkg/run/watch/setup_dev_helm.go +++ /dev/null @@ -1,248 +0,0 @@ -package watch - -import ( - "context" - "fmt" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" - "path/filepath" - "time" - - helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" - "github.com/fluxcd/pkg/apis/meta" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - apierrors "k8s.io/apimachinery/pkg/api/errors" - 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/controller-runtime/pkg/client" -) - -func SetupBucketSourceAndHelm(ctx context.Context, log logger.Logger, kubeClient client.Client, params SetupRunObjectParams) error { - secret, source := createBucketAndSecretObjects(params) - - helm := helmv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevHelmName, - Namespace: params.Namespace, - Annotations: map[string]string{ - "metadata.weave.works/description": "This is a temporary HelmRelease created by GitOps Run. This will be cleaned up when this instance of GitOps Run is ended.", - "metadata.weave.works/run-id": params.SessionName, - "metadata.weave.works/username": params.Username, - }, - }, - Spec: helmv2.HelmReleaseSpec{ - Interval: metav1.Duration{Duration: 30 * 24 * time.Hour}, // 30 days - Chart: helmv2.HelmChartTemplate{ - Spec: helmv2.HelmChartTemplateSpec{ - Chart: params.Path, - ReconcileStrategy: "Revision", - SourceRef: helmv2.CrossNamespaceObjectReference{ - Kind: sourcev1b2.BucketKind, - Name: constants.RunDevBucketName, - }, - // relative to the root of SourceRef - ValuesFiles: []string{ - filepath.Join(params.Path, "values.yaml"), - }, - }, - }, - Timeout: &metav1.Duration{Duration: params.Timeout}, - }, - } - - err := reconcileBucketAndSecretObjects(ctx, log, kubeClient, secret, source) - if err != nil { - return err - } - - // create ks - log.Actionf("Checking HelmRelease %s ...", helm.Name) - - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(&helm), &helm); err != nil && apierrors.IsNotFound(err) { - if err := kubeClient.Create(ctx, &helm); err != nil { - return fmt.Errorf("couldn't create HelmRelease %s: %v", helm.Name, err.Error()) - } else { - log.Successf("Created HelmRelease %s", helm.Name) - } - } else if err == nil { - log.Successf("HelmRelease %s already existed", source.Name) - } - - log.Successf("Setup Bucket Source and HelmRelease successfully") - - return nil -} - -// CleanupBucketSourceAndHelm removes the bucket source and ks -func CleanupBucketSourceAndHelm(ctx context.Context, log logger.Logger, kubeClient client.Client, namespace string) error { - // delete ks - helm := helmv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevHelmName, - Namespace: namespace, - }, - } - - log.Actionf("Deleting HelmRelease %s ...", helm.Name) - - if err := kubeClient.Delete(ctx, &helm); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting HelmRelease %s: %v", helm.Name, err.Error()) - } - } else { - log.Successf("Deleted HelmRelease %s", helm.Name) - } - - cleanupBucketAndSecretObjects(ctx, log, kubeClient, namespace) - - log.Successf("Cleanup Bucket Source and HelmRelease successfully") - - return nil -} - -// ReconcileDevBucketSourceAndHelm reconciles the dev-bucket and dev-helm asynchronously. -func ReconcileDevBucketSourceAndHelm(ctx context.Context, log logger.Logger, kubeClient client.Client, namespace string, timeout time.Duration) error { - const interval = 4 * time.Second - - log.Actionf("Start reconciling %s and %s ...", constants.RunDevBucketName, constants.RunDevHelmName) - - // reconcile dev-bucket - sourceRequestedAt, err := run.RequestReconciliation(ctx, kubeClient, - types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, schema.GroupVersionKind{ - Group: sourcev1b2.GroupVersion.Group, - Version: sourcev1b2.GroupVersion.Version, - Kind: sourcev1b2.BucketKind, - }) - if err != nil { - return err - } - - log.Actionf("Reconciling %s ...", constants.RunDevBucketName) - - // wait for the reconciliation of dev-bucket to be done - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: interval, - Factor: 2, - Jitter: 1, - Steps: 10, - Cap: timeout, - }, func() (done bool, err error) { - devBucket := &sourcev1b2.Bucket{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, devBucket); err != nil { - return false, err - } - - return devBucket.Status.GetLastHandledReconcileRequest() == sourceRequestedAt, nil - }); err != nil { - return err - } - - log.Successf("Reconciled %s", constants.RunDevBucketName) - - // wait for devBucket to be ready - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: interval, - Factor: 2, - Jitter: 1, - Steps: 10, - Cap: timeout, - }, func() (done bool, err error) { - devBucket := &sourcev1b2.Bucket{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, devBucket); err != nil { - return false, err - } - - return apimeta.IsStatusConditionPresentAndEqual(devBucket.Status.Conditions, meta.ReadyCondition, metav1.ConditionTrue), nil - }); err != nil { - return err - } - - log.Successf("Bucket %s is ready", constants.RunDevBucketName) - - // reconcile dev-ks - helmRequestedAt, err := run.RequestReconciliation(ctx, kubeClient, - types.NamespacedName{ - Name: constants.RunDevHelmName, - Namespace: namespace, - }, schema.GroupVersionKind{ - Group: helmv2.GroupVersion.Group, - Version: helmv2.GroupVersion.Version, - Kind: helmv2.HelmReleaseKind, - }) - if err != nil { - return err - } - - log.Actionf("Reconciling %s ...", constants.RunDevHelmName) - - if err := wait.ExponentialBackoff(wait.Backoff{ - Duration: interval, - Factor: 2, - Jitter: 1, - Steps: 10, - Cap: timeout, - }, func() (done bool, err error) { - devHelm := &helmv2.HelmRelease{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevHelmName, - Namespace: namespace, - }, devHelm); err != nil { - return false, err - } - - return devHelm.Status.GetLastHandledReconcileRequest() == helmRequestedAt, nil - }); err != nil { - return err - } - - log.Successf("Reconciled %s", constants.RunDevHelmName) - - devHelm := &helmv2.HelmRelease{} - - devHelmErr := wait.ExponentialBackoff(wait.Backoff{ - Duration: interval, - Factor: 2, - Jitter: 1, - Steps: 10, - Cap: timeout, - }, func() (done bool, err error) { - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevHelmName, - Namespace: namespace, - }, devHelm); err != nil { - return false, err - } - - log.Actionf("Waiting for %s to be ready ...", constants.RunDevHelmName) - - cond := apimeta.FindStatusCondition(devHelm.Status.Conditions, meta.ReadyCondition) - if cond == nil { - return false, nil - } - - if cond.Status != "False" && cond.Status != "True" { - log.Waitingf("Waiting for HelmRelease %s to be ready: %s", devHelm.Name, cond.Message) - return false, nil - } else if cond.Status == "False" { - log.Failuref("HelmRelease %s is not ready: %s", devHelm.Name, cond.Message) - return false, fmt.Errorf("HelmRelease %s is not ready: %s", devHelm.Name, cond.Message) - } - - return true, nil - }) - - return devHelmErr -} diff --git a/pkg/run/watch/setup_dev_ks.go b/pkg/run/watch/setup_dev_ks.go deleted file mode 100644 index 8310c039c2..0000000000 --- a/pkg/run/watch/setup_dev_ks.go +++ /dev/null @@ -1,538 +0,0 @@ -package watch - -import ( - "context" - "errors" - "fmt" - - "io" - "os" - "path/filepath" - "strings" - "time" - - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - "github.com/fluxcd/pkg/apis/meta" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" - "github.com/fsnotify/fsnotify" - "github.com/minio/minio-go/v7" - ignore "github.com/sabhiram/go-gitignore" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run" - "github.com/weaveworks/weave-gitops/pkg/run/constants" - "github.com/weaveworks/weave-gitops/pkg/sourceignore" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/cli-utils/pkg/object" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type SetupRunObjectParams struct { - Namespace string - Path string - Timeout time.Duration - DevBucketPort int32 - SessionName string - Username string - AccessKey []byte - SecretKey []byte - DecryptionKeyFile string -} - -func SetupBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeClient client.Client, params SetupRunObjectParams) error { - secret, source := createBucketAndSecretObjects(params) - - ks := kustomizev1.Kustomization{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevKsName, - Namespace: params.Namespace, - Annotations: map[string]string{ - "metadata.weave.works/description": "This is a temporary Kustomization created by GitOps Run. This will be cleaned up when this instance of GitOps Run is ended.", - "metadata.weave.works/run-id": params.SessionName, - "metadata.weave.works/username": params.Username, - }, - }, - Spec: kustomizev1.KustomizationSpec{ - Interval: metav1.Duration{Duration: 30 * 24 * time.Hour}, // 30 days - Prune: true, // GC the kustomization - SourceRef: kustomizev1.CrossNamespaceSourceReference{ - Kind: sourcev1b2.BucketKind, - Name: constants.RunDevBucketName, - }, - Timeout: &metav1.Duration{Duration: params.Timeout}, - Path: params.Path, - Wait: true, - }, - } - - if err := setupDecryption(ctx, params, kubeClient, &ks); err != nil { - return fmt.Errorf("failed setting up decryption: %w", err) - } - - err := reconcileBucketAndSecretObjects(ctx, log, kubeClient, secret, source) - if err != nil { - return err - } - - // create ks - log.Actionf("Checking Kustomization %s ...", ks.Name) - - if err := kubeClient.Get(ctx, client.ObjectKeyFromObject(&ks), &ks); err != nil && apierrors.IsNotFound(err) { - if err := kubeClient.Create(ctx, &ks); err != nil { - return fmt.Errorf("couldn't create kustomization %s: %v", ks.Name, err.Error()) - } else { - log.Successf("Created Kustomization %s", ks.Name) - } - } else if err == nil { - log.Successf("Kustomization %s already existed", source.Name) - } - - log.Successf("Setup Bucket Source and Kustomization successfully") - - return nil -} - -func setupDecryption(ctx context.Context, params SetupRunObjectParams, kubeClient client.Client, ks *kustomizev1.Kustomization) error { - if params.DecryptionKeyFile == "" { - return nil - } - - ageKeyData, err := os.ReadFile(params.DecryptionKeyFile) - if err != nil { - return fmt.Errorf("failed reading age key file: %w", err) - } - - var secretKey string - - switch filepath.Ext(params.DecryptionKeyFile) { - case ".agekey": - secretKey = "age.agekey" - case ".asc": - secretKey = "identity.asc" - default: - return fmt.Errorf("failed determining decryption key type from filename %s", filepath.Base(params.DecryptionKeyFile)) - } - - decSecret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevKsDecryption, - Namespace: params.Namespace, - }, - Data: map[string][]byte{ - secretKey: ageKeyData, - }, - } - if err := kubeClient.Create(ctx, &decSecret); err != nil { - return fmt.Errorf("failed creating age key Secret: %w", err) - } - - ks.Spec.Decryption = &kustomizev1.Decryption{ - Provider: "sops", - SecretRef: &meta.LocalObjectReference{ - Name: decSecret.Name, - }, - } - - return nil -} - -// SyncDir recursively uploads all files in a directory to an S3 bucket with minio library -func SyncDir(ctx context.Context, log logger.Logger, dir, bucket string, client *minio.Client, ignorer *ignore.GitIgnore) error { - log.Actionf("Refreshing bucket %s ...", bucket) - - if err := client.RemoveBucketWithOptions(ctx, bucket, minio.RemoveBucketOptions{ - ForceDelete: true, - }); err != nil { - // if error is not bucket not found, return error - if !strings.Contains(err.Error(), "NoSuchBucket") { - return err - } - } - - if err := client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}); err != nil { - return err - } - - uploadCount := 0 - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - log.Failuref("Error walking directory: %v", err) - return err - } - - if info.IsDir() { - // if it's a hidden directory, ignore it - if strings.HasPrefix(info.Name(), ".") { - return filepath.SkipDir - } - - return nil - } - - objectName, err := filepath.Rel(dir, path) - if err != nil { - log.Failuref("Error getting relative path: %v", err) - return err - } - if ignorer.MatchesPath(path) { - return nil - } - // upload the file - _, err = client.FPutObject(ctx, bucket, objectName, path, minio.PutObjectOptions{}) - - if err != nil { - if errors.Is(err, context.Canceled) { - return err - } - errResp, ok := err.(minio.ErrorResponse) - if ok && errResp.Code == "MissingContentLength" { - // This happens when the file was empty - this is OK - return nil - } - // Report the error, but continue anyway - this could be e.g. - // a file with odd permissions, which isn't necessarily a problem - log.Failuref("Couldn't upload %v: %v", path, err) - return nil - } - uploadCount++ - if uploadCount%10 == 0 { - fmt.Print(".") - } - return nil - }) - - fmt.Println() - log.Actionf("Uploaded %d files", uploadCount) - - if err != nil && !errors.Is(err, context.Canceled) { - log.Failuref("Error syncing directory: %v", err) - return err - } - - return nil -} - -// CleanupBucketSourceAndKS removes the bucket source and ks -func CleanupBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeClient client.Client, namespace string) error { - // delete ks - ks := kustomizev1.Kustomization{ - ObjectMeta: metav1.ObjectMeta{ - Name: constants.RunDevKsName, - Namespace: namespace, - }, - } - - log.Actionf("Deleting Kustomization %s ...", ks.Name) - - if err := kubeClient.Delete(ctx, &ks); err != nil { - if !apierrors.IsNotFound(err) { - log.Failuref("Error deleting Kustomization %s: %v", ks.Name, err.Error()) - } - } else { - log.Successf("Deleted Kustomization %s", ks.Name) - } - - cleanupBucketAndSecretObjects(ctx, log, kubeClient, namespace) - - log.Successf("Cleanup Bucket Source and Kustomization successfully") - - return nil -} - -// findConditionMessages finds the messages in the condition of objects in the inventory. -func findConditionMessages(ctx context.Context, kubeClient client.Client, ks *kustomizev1.Kustomization) ([]string, error) { - if ks.Status.Inventory == nil { - return nil, fmt.Errorf("inventory is nil") - } - - gvks := map[string]schema.GroupVersionKind{} - // collect gvk of the objects - for _, entry := range ks.Status.Inventory.Entries { - objMeta, err := object.ParseObjMetadata(entry.ID) - if err != nil { - return nil, fmt.Errorf("invalid inventory item '%s', error: %w", entry.ID, err) - } - - gvkID := strings.Join([]string{objMeta.GroupKind.Group, entry.Version, objMeta.GroupKind.Kind}, "_") - - if _, exist := gvks[gvkID]; !exist { - gvks[gvkID] = schema.GroupVersionKind{ - Group: objMeta.GroupKind.Group, - Version: entry.Version, - Kind: objMeta.GroupKind.Kind, - } - } - } - - var messages []string - - for _, gvk := range gvks { - unstructuredList := &unstructured.UnstructuredList{} - unstructuredList.SetGroupVersionKind(gvk) - - if err := kubeClient.List(ctx, unstructuredList, - client.MatchingLabelsSelector{ - Selector: labels.Set( - map[string]string{ - "kustomize.toolkit.fluxcd.io/name": ks.Name, - "kustomize.toolkit.fluxcd.io/namespace": ks.Namespace, - }, - ).AsSelector(), - }, - ); err != nil { - return nil, err - } - - for _, u := range unstructuredList.Items { - if conditions, found, err := unstructured.NestedSlice(u.UnstructuredContent(), "status", "conditions"); err == nil && found { - for _, condition := range conditions { - c := condition.(map[string]interface{}) - if status, found, err := unstructured.NestedString(c, "status"); err == nil && found { - if status != "True" { - if message, found, err := unstructured.NestedString(c, "message"); err == nil && found { - messages = append(messages, fmt.Sprintf("%s %s/%s: %s", u.GetKind(), u.GetNamespace(), u.GetName(), message)) - } - } - } - } - } - } - } - - return messages, nil -} - -func WatchDirsForFileWalker(watcher *fsnotify.Watcher, ignorer *ignore.GitIgnore) func(path string, info os.FileInfo, err error) error { - return func(path string, info os.FileInfo, err error) error { - if err != nil { - return fmt.Errorf("error walking path: %v", err) - } - - if info.IsDir() { - // if it's a hidden directory, ignore it - if strings.HasPrefix(info.Name(), ".") { - return filepath.SkipDir - } - - if ignorer.MatchesPath(path) { - return filepath.SkipDir - } - - if err := watcher.Add(path); err != nil { - return err - } - } - - return nil - } -} - -// InitializeRootDir initializes the root directory (creates the .sourceignore file in it). -func InitializeRootDir(log logger.Logger, rootPath string) error { - stat, err := os.Stat(rootPath) - - if err != nil { - return err - } else if !stat.IsDir() { - return fmt.Errorf("root must be a directory") - } else { - f, err := os.Open(rootPath) - if err != nil { - return err - } - - _, err = f.Readdirnames(1) - if err != nil && !errors.Is(err, io.EOF) { - return err - } - } - - err = sourceignore.CreateIgnoreFile(rootPath, sourceignore.IgnoreFilename, []string{}) - if err == nil { - log.Successf("%s file created. Please add ignore patterns to ignore specific YAML files or directories during validation to it", sourceignore.IgnoreFilename) - } - - return err -} - -// InitializeTargetDir initializes the target directory (creates the entrypoint Kustomization in it). -func InitializeTargetDir(targetPath string) error { - shouldCreate := false - stat, err := os.Stat(targetPath) - - if err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } else if err != nil { - err := os.MkdirAll(targetPath, 0o755) - if err != nil { - return err - } - shouldCreate = true - } else if !stat.IsDir() { - return fmt.Errorf("target must be a directory") - } else { - f, err := os.Open(targetPath) - if err != nil { - return err - } - - _, err = f.Readdirnames(1) - if err != nil && errors.Is(err, io.EOF) { - shouldCreate = true - } else if err != nil { - return err - } - } - - if shouldCreate { - f, err := os.Create(filepath.Join(targetPath, "kustomization.yaml")) - if err != nil { - return fmt.Errorf("error creating entrypoint kustomization.yaml: %w", err) - } - - _, err = f.Write([]byte(`--- -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: [] # 👋 Start adding the resources you want to sync here -`)) - if err != nil { - return err - } - } - - return nil -} - -// ReconcileDevBucketSourceAndKS reconciles the dev-bucket and dev-ks asynchronously. -func ReconcileDevBucketSourceAndKS(ctx context.Context, log logger.Logger, kubeClient client.Client, namespace string, timeout time.Duration) error { - const interval = 3 * time.Second / 2 - - // reconcile dev-bucket - sourceRequestedAt, err := run.RequestReconciliation(ctx, kubeClient, - types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, schema.GroupVersionKind{ - Group: sourcev1b2.GroupVersion.Group, - Version: sourcev1b2.GroupVersion.Version, - Kind: sourcev1b2.BucketKind, - }) - if err != nil { - return err - } - - // wait for the reconciliation of dev-bucket to be done - if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { - devBucket := &sourcev1b2.Bucket{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, devBucket); err != nil { - return false, err - } - - return devBucket.Status.GetLastHandledReconcileRequest() == sourceRequestedAt, nil - }); err != nil { - return err - } - - // wait for devBucket to be ready - if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { - devBucket := &sourcev1b2.Bucket{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevBucketName, - Namespace: namespace, - }, devBucket); err != nil { - return false, err - } - return apimeta.IsStatusConditionPresentAndEqual(devBucket.Status.Conditions, meta.ReadyCondition, metav1.ConditionTrue), nil - }); err != nil { - return err - } - - // reconcile dev-ks - ksRequestedAt, err := run.RequestReconciliation(ctx, kubeClient, - types.NamespacedName{ - Name: constants.RunDevKsName, - Namespace: namespace, - }, schema.GroupVersionKind{ - Group: kustomizev1.GroupVersion.Group, - Version: kustomizev1.GroupVersion.Version, - Kind: kustomizev1.KustomizationKind, - }) - if err != nil { - return err - } - - if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { - devKs := &kustomizev1.Kustomization{} - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevKsName, - Namespace: namespace, - }, devKs); err != nil { - return false, err - } - - return devKs.Status.GetLastHandledReconcileRequest() == ksRequestedAt, nil - }); err != nil { - return err - } - - devKs := &kustomizev1.Kustomization{} - devKsErr := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(_ context.Context) (bool, error) { - if err := kubeClient.Get(ctx, types.NamespacedName{ - Name: constants.RunDevKsName, - Namespace: namespace, - }, devKs); err != nil { - return false, err - } - - healthy := apimeta.IsStatusConditionPresentAndEqual( - devKs.Status.Conditions, - kustomizev1.HealthyCondition, - metav1.ConditionTrue, - ) - return healthy, nil - }) - - if devKsErr != nil { - messages, err := findConditionMessages(ctx, kubeClient, devKs) - if err != nil { - return err - } - - for _, msg := range messages { - log.Failuref(msg) - } - } - - return devKsErr -} - -func CreateIgnorer(gitRootDir string) *ignore.GitIgnore { - ignoreFile := filepath.Join(gitRootDir, ".gitignore") - - var ignorer *ignore.GitIgnore = nil - if _, err := os.Stat(ignoreFile); err == nil { - ignorer, err = ignore.CompileIgnoreFile(ignoreFile) - if err != nil { - // If we couldn't parse gitignore, just ignore nothing - ignorer = nil - } - } - - if ignorer == nil { - // Whether there was no gitignore file or the one that was there was broken, - // fall back to just no ignore lines - this is just a pass-through - ignorer = ignore.CompileIgnoreLines() - } - - return ignorer -} diff --git a/pkg/run/watch/setup_dev_ks_test.go b/pkg/run/watch/setup_dev_ks_test.go deleted file mode 100644 index 2668e6f38a..0000000000 --- a/pkg/run/watch/setup_dev_ks_test.go +++ /dev/null @@ -1,370 +0,0 @@ -package watch - -import ( - "context" - "os" - "path/filepath" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - "github.com/fluxcd/pkg/apis/meta" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - - "github.com/weaveworks/weave-gitops/pkg/kube" - "github.com/weaveworks/weave-gitops/pkg/logger" - "github.com/weaveworks/weave-gitops/pkg/run/constants" -) - -// mock controller-runtime client -type mockClientForFindConditionMessages struct { - client.Client -} - -// mock client.List -func (c *mockClientForFindConditionMessages) List(_ context.Context, list client.ObjectList, _ ...client.ListOption) error { // m - list.(*unstructured.UnstructuredList).Items = []unstructured.Unstructured{ - { - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "name": "deployment", - "namespace": "default", - }, - "status": map[string]interface{}{ - "conditions": []interface{}{ - map[string]interface{}{ - "type": "Ready", - "status": "False", - "message": "This is message", - }, - map[string]interface{}{ - "type": "Healthy", - "status": "True", - "message": "no error", - }, - }, - }, - }, - }, - { - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "name": "app2", - "namespace": "default", - }, - "status": map[string]interface{}{ - "conditions": []interface{}{ - map[string]interface{}{ - "type": "Ready", - "status": "True", - "message": "no error", - }, - map[string]interface{}{ - "type": "Healthy", - "status": "True", - "message": "no error", - }, - }, - }, - }, - }, - { - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "name": "app3", - "namespace": "default", - }, - "status": map[string]interface{}{ - "conditions": []interface{}{ - map[string]interface{}{ - "type": "Ready", - "status": "False", - "message": "app 3 error", - }, - map[string]interface{}{ - "type": "Healthy", - "status": "False", - "message": "time out", - }, - }, - }, - }, - }, - } - - return nil -} - -func deleteObjects(ctx context.Context, o ...client.Object) { - for _, obj := range o { - Expect( - k8sClient.Delete(ctx, obj), - ).To( - Succeed(), "failed deleting object %s/%s", obj.GetNamespace(), obj.GetName()) - - if _, isNamespace := obj.(*corev1.Namespace); isNamespace { - // Namespaces can't be deleted in envtest: - // https://book.kubebuilder.io/reference/envtest.html#namespace-usage-limitation - return - } - - Eventually(k8sClient.Get, "10s"). - WithArguments(ctx, client.ObjectKeyFromObject(obj), obj). - Should(WithTransform(apierrors.IsNotFound, BeTrue()), - "object %s/%s not deleted", obj.GetNamespace(), obj.GetName()) - } -} - -var _ = Describe("SetupBucketSourceAndKS", func() { - log := logger.NewCLILogger(GinkgoWriter) - - It("fails when Namespace isn't set", func() { - Expect( - SetupBucketSourceAndKS(context.Background(), log, k8sClient, SetupRunObjectParams{}), - ).To( - HaveOccurred(), "expected function call to fail") - }) - - It("fails when user lacks permission in Namespace", func() { - // create a user that has no permission on the cluster - authUser, err := k8sEnv.Env.AddUser(envtest.User{ - Name: "no-permission", - }, nil) - Expect(err).NotTo(HaveOccurred(), "failed creating unauthenticated user") - - scheme, err := kube.CreateScheme() - Expect(err).NotTo(HaveOccurred()) - - unauthClient, err := client.New(authUser.Config(), client.Options{ - Scheme: scheme, - }) - Expect(err).NotTo(HaveOccurred()) - - testNS := corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ - GenerateName: "setupbucketsourceandks-", - }, - } - Expect(k8sClient.Create(context.Background(), &testNS)).To(Succeed(), "failed creating test Namespace") - defer deleteObjects(context.Background(), &testNS) - - Expect( - SetupBucketSourceAndKS(context.Background(), log, unauthClient, SetupRunObjectParams{ - Namespace: testNS.Name, - }), - ).To( - HaveOccurred(), "expected function call to fail") - }) - - Describe("decryption setup", func() { - It("fails with non-existent file name", func() { - testNS := corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ - GenerateName: "setupbucketsourceandks-", - }, - } - Expect(k8sClient.Create(context.Background(), &testNS)).To(Succeed(), "failed creating test Namespace") - defer deleteObjects(context.Background(), &testNS) - - Expect(SetupBucketSourceAndKS(context.Background(), log, k8sClient, SetupRunObjectParams{ - Namespace: testNS.Name, - DecryptionKeyFile: "/does/not/exist", - })).To(MatchError(HavePrefix("failed setting up decryption")), "expected a failure") - }) - - It("fails with unknown file extension", func() { - testNS := corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ - GenerateName: "setupbucketsourceandks-", - }, - } - Expect(k8sClient.Create(context.Background(), &testNS)).To(Succeed(), "failed creating test Namespace") - defer deleteObjects(context.Background(), &testNS) - - Expect(SetupBucketSourceAndKS(context.Background(), log, k8sClient, SetupRunObjectParams{ - Namespace: testNS.Name, - DecryptionKeyFile: "./testdata/emptyfile", - })).To(MatchError(ContainSubstring("failed determining decryption key type from filename")), "expected a failure") - }) - - DescribeTable("creates a Secret and configures the Kustomization", - func(filename, secretKey string) { - testNS := corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ - GenerateName: "setupbucketsourceandks-", - }, - } - Expect(k8sClient.Create(context.Background(), &testNS)).To(Succeed(), "failed creating test Namespace") - defer deleteObjects(context.Background(), &testNS) - - Expect(SetupBucketSourceAndKS(context.Background(), log, k8sClient, SetupRunObjectParams{ - Namespace: testNS.Name, - DecryptionKeyFile: filename, - }), - ).To(Succeed(), "function failed unexpectedly") - - var decSecret corev1.Secret - Expect( - k8sClient.Get(context.Background(), client.ObjectKey{ - Namespace: testNS.Name, - Name: constants.RunDevKsDecryption, - }, &decSecret), - ).To(Succeed(), "failed to retrieve decryption Secret") - - Expect(decSecret.Data).To(HaveKeyWithValue(secretKey, []byte{}), "unexpected data in decryption Secret") - - var ks kustomizev1.Kustomization - Expect( - k8sClient.Get(context.Background(), client.ObjectKey{ - Namespace: testNS.Name, - Name: constants.RunDevKsName, - }, &ks), - ).To(Succeed(), "failed to retrieve Kustomization") - - Expect(ks.Spec.Decryption).ToNot(BeNil(), "decryption spec not set") - Expect(ks.Spec.Decryption.Provider).To(Equal("sops"), "unexpected decryption provider set") - Expect(ks.Spec.Decryption.SecretRef).ToNot(BeNil(), "decryption spec missing Secret reference") - Expect(ks.Spec.Decryption.SecretRef).To(Equal(&meta.LocalObjectReference{ - Name: decSecret.Name, - }), "decryption spec has invalid Secret reference") - }, - Entry("with GPG file", "./testdata/emptyfile.asc", "identity.asc"), - Entry("with age file", "./testdata/emptyfile.agekey", "age.agekey"), - ) - }) - - It("returns no error", func() { - testNS := &corev1.Namespace{ - ObjectMeta: v1.ObjectMeta{ - GenerateName: "setupbucketsourceandks-", - }, - } - Expect(k8sClient.Create(context.Background(), testNS)).To(Succeed(), "failed creating test Namespace") - defer deleteObjects(context.Background(), testNS) - - err := SetupBucketSourceAndKS(context.Background(), log, k8sClient, SetupRunObjectParams{ - Namespace: testNS.Name, - }) - Expect(err).ToNot(HaveOccurred(), "setting up dev bucket and Kustomization failed") - }) -}) - -var _ = Describe("findConditionMessages", func() { - It("returns the condition messages", func() { - client := &mockClientForFindConditionMessages{} - ks := &kustomizev1.Kustomization{ - Spec: kustomizev1.KustomizationSpec{}, - Status: kustomizev1.KustomizationStatus{ - Inventory: &kustomizev1.ResourceInventory{ - Entries: []kustomizev1.ResourceRef{ - { - ID: "default_deployment_apps_Deployment", - Version: "v1", - }, - { - ID: "default_app2_apps_Deployment", - Version: "v1", - }, - { - ID: "default_app3_apps_Deployment", - Version: "v1", - }, - }, - }, - }, - } - messages, err := findConditionMessages(context.Background(), client, ks) - Expect(err).ToNot(HaveOccurred()) - Expect(messages).To(Equal([]string{ - "Deployment default/deployment: This is message", - "Deployment default/app3: app 3 error", - "Deployment default/app3: time out", - })) - }) -}) - -var _ = Describe("InitializeTargetDir", func() { - It("creates a file in an empty directory", func() { - dir, err := os.MkdirTemp("", "target-dir") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - - kustomizationPath := filepath.Join(dir, "kustomization.yaml") - - _, err = os.Stat(kustomizationPath) - Expect(err).To(HaveOccurred()) // File not created yet - - err = InitializeTargetDir(dir) - Expect(err).ToNot(HaveOccurred()) - - fi, err := os.Stat(kustomizationPath) - Expect(err).ToNot(HaveOccurred()) - - err = InitializeTargetDir(dir) - Expect(err).ToNot(HaveOccurred()) - - fi2, err := os.Stat(kustomizationPath) - Expect(err).ToNot(HaveOccurred()) - Expect(fi2.ModTime()).To(Equal(fi.ModTime())) // File not updated - }) - - It("creates a file in nonexistent directory", func() { - dir, err := os.MkdirTemp("", "target-dir") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - - childDir := filepath.Join(dir, "subdirectory", "subsubdirectory") - _, err = os.Stat(childDir) - Expect(err).To(HaveOccurred()) // Directory not created yet - - kustomizationPath := filepath.Join(childDir, "kustomization.yaml") - - err = InitializeTargetDir(childDir) - Expect(err).ToNot(HaveOccurred()) - - _, err = os.Stat(kustomizationPath) - Expect(err).ToNot(HaveOccurred()) - }) - - It("throws an error if pointed at a file", func() { - dir, err := os.MkdirTemp("", "target-dir") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - - kustomizationPath := filepath.Join(dir, "kustomization.yaml") - err = InitializeTargetDir(dir) - Expect(err).ToNot(HaveOccurred()) - - err = InitializeTargetDir(kustomizationPath) - Expect(err).To(HaveOccurred()) - }) -}) - -var _ = Describe("CreateIgnorer", func() { - It("finds and parses existing gitignore", func() { - str, err := filepath.Abs("../../..") - Expect(err).ToNot(HaveOccurred()) - ignorer := CreateIgnorer(str) - Expect(ignorer.MatchesPath("pkg/server")).To(Equal(false)) - Expect(ignorer.MatchesPath("temp~")).To(Equal(true)) - Expect(ignorer.MatchesPath("bin/gitops")).To(Equal(true)) - }) - It("doesn't mind no gitignore", func() { - str, err := filepath.Abs(".") - Expect(err).ToNot(HaveOccurred()) - ignorer := CreateIgnorer(str) - Expect(ignorer.MatchesPath("bin/gitops")).To(Equal(false)) - }) -}) From 97100240deeb73dde870790a987abda52e20ee4d Mon Sep 17 00:00:00 2001 From: yiannis Date: Wed, 1 Nov 2023 15:50:30 +0000 Subject: [PATCH 19/23] build(deps): Remove gitops bucket server from build --- .github/workflows/release.yaml | 1 - Makefile | 5 -- cmd/gitops-bucket-server/main.go | 88 -------------------------------- 3 files changed, 94 deletions(-) delete mode 100644 cmd/gitops-bucket-server/main.go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1118cf8211..d9ae5e378f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -140,7 +140,6 @@ jobs: echo "BRANCH=$GITHUB_EVENT_PULL_REQUEST_HEAD_REF" >> $GITHUB_ENV echo "GORELEASER_PREVIOUS_TAG=$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))" >> $GITHUB_ENV echo "GORELEASER_CURRENT_TAG=${{ needs.tag-release.outputs.version }}" >> $GITHUB_ENV - echo "DEV_BUCKET_CONTAINER_IMAGE=$(make echo-dev-bucket-container)" >> $GITHUB_ENV echo "FLUX_VERSION=$(make echo-flux-version)" >> $GITHUB_ENV echo "CHART_VERSION=$(yq e '.version' charts/gitops-server/Chart.yaml)" >> $GITHUB_ENV - name: "Make All" diff --git a/Makefile b/Makefile index 56d3e5ae49..acc55d63aa 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,6 @@ GIT_COMMIT?=$(shell which git > /dev/null && git log -n1 --pretty='%h') VERSION?=$(shell which git > /dev/null && git describe --always --match "v*") FLUX_VERSION=2.0.1 CHART_VERSION=$(shell which yq > /dev/null && yq e '.version' charts/gitops-server/Chart.yaml) -DEV_BUCKET_CONTAINER_IMAGE?=ghcr.io/weaveworks/gitops-bucket-server@sha256:9fa2a68032b9d67197a3d41a46b5029ffdf9a7bc415e4e7e9794faec8bc3b8e4 TIER=oss # Go build args @@ -20,7 +19,6 @@ LDFLAGS?=-X github.com/weaveworks/weave-gitops/cmd/gitops/version.Branch=$(BRANC -X github.com/weaveworks/weave-gitops/cmd/gitops/version.GitCommit=$(GIT_COMMIT) \ -X github.com/weaveworks/weave-gitops/cmd/gitops/version.Version=$(VERSION) \ -X github.com/weaveworks/weave-gitops/pkg/version.FluxVersion=$(FLUX_VERSION) \ - -X github.com/weaveworks/weave-gitops/pkg/run/watch.DevBucketContainerImage=$(DEV_BUCKET_CONTAINER_IMAGE) \ -X github.com/weaveworks/weave-gitops/pkg/analytics.Tier=$(TIER) \ -X github.com/weaveworks/weave-gitops/core/server.Branch=$(BRANCH) \ -X github.com/weaveworks/weave-gitops/core/server.Buildtime=$(BUILD_TIME) \ @@ -225,9 +223,6 @@ echo-ldflags: echo-flux-version: @echo $(FLUX_VERSION) -echo-dev-bucket-container: - @echo $(DEV_BUCKET_CONTAINER_IMAGE) - download-test-crds: group_resources="source/helmrepositories source/buckets source/gitrepositories source/helmcharts source/ocirepositories"; \ for group_resource in $$group_resources; do \ diff --git a/cmd/gitops-bucket-server/main.go b/cmd/gitops-bucket-server/main.go deleted file mode 100644 index 2ad9fe27e0..0000000000 --- a/cmd/gitops-bucket-server/main.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "context" - "flag" - "log" - "os" - "os/signal" - "syscall" - - "github.com/johannesboyne/gofakes3" - "github.com/johannesboyne/gofakes3/backend/s3mem" - "github.com/weaveworks/weave-gitops/pkg/http" - "github.com/weaveworks/weave-gitops/pkg/s3" -) - -func main() { - logger := log.New(os.Stdout, "", 0) - - awsAccessKeyID := os.Getenv("AWS_ACCESS_KEY_ID") - if awsAccessKeyID == "" { - minioRootUser := os.Getenv("MINIO_ROOT_USER") - if minioRootUser == "" { - logger.Fatal("AWS_ACCESS_KEY_ID or MINIO_ROOT_USER must be set") - return - } - - awsAccessKeyID = minioRootUser - } - - awsSecretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") - if awsSecretAccessKey == "" { - minioRootPassword := os.Getenv("MINIO_ROOT_PASSWORD") - if minioRootPassword == "" { - logger.Fatal("AWS_SECRET_ACCESS_KEY or MINIO_ROOT_PASSWORD must be set") - return - } - - awsSecretAccessKey = minioRootPassword - } - - ctx, cancel := signal.NotifyContext( - context.Background(), - syscall.SIGINT, - syscall.SIGTERM) - defer cancel() - - s3Server := gofakes3.New(s3mem.New(), - gofakes3.WithAutoBucket(true), - gofakes3.WithLogger( - gofakes3.StdLog( - logger, - gofakes3.LogErr, - gofakes3.LogWarn, - gofakes3.LogInfo, - ))).Server() - - var ( - httpPort, httpsPort int - certFile, keyFile string - ) - - flag.IntVar(&httpPort, "http-port", 9000, "TCP port to listen on for HTTP connections") - flag.IntVar(&httpsPort, "https-port", 9443, "TCP port to listen on for HTTPS connections") - flag.StringVar(&certFile, "cert-file", "", "Path to the HTTPS server certificate file") - flag.StringVar(&keyFile, "key-file", "", "Path to the HTTPS server certificate key file") - flag.Parse() - - if certFile == "" { - logger.Fatalf("please specify the path to the HTTPS server certificate file") - } - - if keyFile == "" { - logger.Fatalf("please specify the path to the HTTPS server certificate key file") - } - - srv := http.MultiServer{ - HTTPPort: httpPort, - HTTPSPort: httpsPort, - CertFile: certFile, - KeyFile: keyFile, - Logger: logger, - } - - if err := srv.Start(ctx, s3.AuthMiddleware(awsAccessKeyID, awsSecretAccessKey, s3Server)); err != nil { - logger.Fatalf("server exited unexpectedly: %s", err) - } -} From d7e730bd813bd606e9eeb5a206798e63c6efe87a Mon Sep 17 00:00:00 2001 From: yiannis Date: Thu, 2 Nov 2023 10:21:05 +0000 Subject: [PATCH 20/23] chore: Remove unused code previously used for GitOps Run --- pkg/run/git_exec.go | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 pkg/run/git_exec.go diff --git a/pkg/run/git_exec.go b/pkg/run/git_exec.go deleted file mode 100644 index 1d6c0d7fdf..0000000000 --- a/pkg/run/git_exec.go +++ /dev/null @@ -1,44 +0,0 @@ -package run - -import ( - "os/exec" - "strings" -) - -func GetBranchName() (string, error) { - cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - out, err := cmd.Output() - - if err != nil { - return "", err - } - - return strings.TrimSpace(string(out)), nil -} - -func GetCommitID() (string, error) { - cmd := exec.Command("git", "rev-parse", "HEAD") - out, err := cmd.Output() - - if err != nil { - return "", err - } - - commitID := strings.TrimSpace(string(out)) - if len(commitID) > 8 { - commitID = commitID[:8] - } - - return commitID, nil -} - -func IsDirty() (bool, error) { - cmd := exec.Command("git", "status", "--porcelain") - out, err := cmd.Output() - - if err != nil { - return false, err - } - - return len(out) > 0, nil -} From 74f21a00aa8554cf140dcdf33085feb1c15b001f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:36:07 +0000 Subject: [PATCH 21/23] build(deps): Bump @babel/traverse from 7.20.13 to 7.23.2 in /website Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.13 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- website/yarn.lock | 133 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 14 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 7f8b4f55c3..c547b27e4f 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -145,6 +145,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.20.14" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" @@ -202,6 +210,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -267,6 +285,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -282,6 +305,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -289,6 +320,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" @@ -377,16 +415,33 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -420,11 +475,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.20.7": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1154,19 +1223,28 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -1179,6 +1257,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -1711,6 +1798,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -1729,6 +1821,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -1737,6 +1834,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -2884,7 +2989,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== From e2cabfc6c2570d538199af2af123bb60c36e77ed Mon Sep 17 00:00:00 2001 From: AsmaaNabilBakr Date: Sun, 16 Jul 2023 10:43:52 +0300 Subject: [PATCH 22/23] fix duplicate icons --- ui/components/Icon.tsx | 4 ++-- ui/components/NavIcons/PoliciesIcon.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index dced593c30..6636375320 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -265,10 +265,10 @@ function getIcon(i: IconType) { return PlayIcon; case IconType.PoliciesIcon: - return PoliciesIcon; + return () => ; case IconType.Policy: - return Policy; + return () => ; case IconType.PolicyConfigsIcon: return PolicyConfigsIcon; diff --git a/ui/components/NavIcons/PoliciesIcon.tsx b/ui/components/NavIcons/PoliciesIcon.tsx index f3952bfb59..52c319dd41 100644 --- a/ui/components/NavIcons/PoliciesIcon.tsx +++ b/ui/components/NavIcons/PoliciesIcon.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { useTheme } from "styled-components"; function PoliciesIcon({ filled }) { const theme = useTheme(); - return ( Date: Mon, 6 Nov 2023 11:55:26 +0200 Subject: [PATCH 23/23] remove extra space --- ui/components/Icon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/Icon.tsx b/ui/components/Icon.tsx index b095bd856a..6636375320 100644 --- a/ui/components/Icon.tsx +++ b/ui/components/Icon.tsx @@ -390,4 +390,4 @@ export default styled(Icon)` img { width: ${(props) => props.theme.spacing[props.size as any]}; } -`; \ No newline at end of file +`;