From f5fa9ff2703f2a0f1c2ff56dce20f7940589f508 Mon Sep 17 00:00:00 2001 From: RoyUP9 <87927115+RoyUP9@users.noreply.github.com> Date: Wed, 9 Mar 2022 17:52:55 +0200 Subject: [PATCH] Added mizu install template (#884) --- cli/apiserver/provider.go | 50 +++----------------- cli/bucket/provider.go | 42 +++++++++++++++++ cli/cmd/common.go | 8 ++-- cli/cmd/install.go | 10 +--- cli/cmd/installRunner.go | 19 ++++++++ cli/config/configStruct.go | 1 + cli/config/configStructs/installConfig.go | 6 +++ cli/utils/httpUtils.go | 47 +++++++++++++++++++ shared/kubernetes/mizuTapperSyncer.go | 13 ++++-- shared/kubernetes/provider.go | 57 +++++++++++++++++++++-- 10 files changed, 189 insertions(+), 64 deletions(-) create mode 100644 cli/bucket/provider.go create mode 100644 cli/cmd/installRunner.go create mode 100644 cli/config/configStructs/installConfig.go create mode 100644 cli/utils/httpUtils.go diff --git a/cli/apiserver/provider.go b/cli/apiserver/provider.go index b4fd4b394..5f277926f 100644 --- a/cli/apiserver/provider.go +++ b/cli/apiserver/provider.go @@ -4,11 +4,10 @@ import ( "bytes" "encoding/json" "fmt" - "io" + "github.com/up9inc/mizu/cli/utils" "io/ioutil" "net/http" "net/url" - "strings" "time" "github.com/up9inc/mizu/shared/kubernetes" @@ -59,7 +58,7 @@ func (provider *Provider) TestConnection() error { func (provider *Provider) isReachable() (bool, error) { echoUrl := fmt.Sprintf("%s/echo", provider.url) - if _, err := provider.get(echoUrl); err != nil { + if _, err := utils.Get(echoUrl, provider.client); err != nil { return false, err } else { return true, nil @@ -72,7 +71,7 @@ func (provider *Provider) ReportTapperStatus(tapperStatus shared.TapperStatus) e if jsonValue, err := json.Marshal(tapperStatus); err != nil { return fmt.Errorf("failed Marshal the tapper status %w", err) } else { - if _, err := provider.post(tapperStatusUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil { + if _, err := utils.Post(tapperStatusUrl, "application/json", bytes.NewBuffer(jsonValue), provider.client); err != nil { return fmt.Errorf("failed sending to API server the tapped pods %w", err) } else { logger.Log.Debugf("Reported to server API about tapper status: %v", tapperStatus) @@ -89,7 +88,7 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error { if jsonValue, err := json.Marshal(podInfos); err != nil { return fmt.Errorf("failed Marshal the tapped pods %w", err) } else { - if _, err := provider.post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil { + if _, err := utils.Post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue), provider.client); err != nil { return fmt.Errorf("failed sending to API server the tapped pods %w", err) } else { logger.Log.Debugf("Reported to server API about %d taped pods successfully", len(podInfos)) @@ -101,7 +100,7 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error { func (provider *Provider) GetGeneralStats() (map[string]interface{}, error) { generalStatsUrl := fmt.Sprintf("%s/status/general", provider.url) - response, requestErr := provider.get(generalStatsUrl) + response, requestErr := utils.Get(generalStatsUrl, provider.client) if requestErr != nil { return nil, fmt.Errorf("failed to get general stats for telemetry, err: %w", requestErr) } @@ -126,7 +125,7 @@ func (provider *Provider) GetVersion() (string, error) { Method: http.MethodGet, URL: versionUrl, } - statusResp, err := provider.do(req) + statusResp, err := utils.Do(req, provider.client) if err != nil { return "", err } @@ -139,40 +138,3 @@ func (provider *Provider) GetVersion() (string, error) { return versionResponse.Ver, nil } - -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -func (provider *Provider) get(url string) (*http.Response, error) { - return provider.checkError(provider.client.Get(url)) -} - -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -func (provider *Provider) post(url, contentType string, body io.Reader) (*http.Response, error) { - return provider.checkError(provider.client.Post(url, contentType, body)) -} - -// When err is nil, resp always contains a non-nil resp.Body. -// Caller should close resp.Body when done reading from it. -func (provider *Provider) do(req *http.Request) (*http.Response, error) { - return provider.checkError(provider.client.Do(req)) -} - -func (provider *Provider) checkError(response *http.Response, errInOperation error) (*http.Response, error) { - if (errInOperation != nil) { - return response, errInOperation - // Check only if status != 200 (and not status >= 300). Agent APIs return only 200 on success. - } else if response.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(response.Body) - response.Body.Close() - response.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind - if err != nil { - return response, err - } - - errorMsg := strings.ReplaceAll((string(body)), "\n", ";") - return response, fmt.Errorf("got response with status code: %d, body: %s", response.StatusCode, errorMsg) - } - - return response, nil -} diff --git a/cli/bucket/provider.go b/cli/bucket/provider.go new file mode 100644 index 000000000..e007952a0 --- /dev/null +++ b/cli/bucket/provider.go @@ -0,0 +1,42 @@ +package bucket + +import ( + "fmt" + "github.com/up9inc/mizu/cli/utils" + "io/ioutil" + "net/http" + "time" +) + +type Provider struct { + url string + client *http.Client +} + +const DefaultTimeout = 2 * time.Second + +func NewProvider(url string, timeout time.Duration) *Provider { + return &Provider{ + url: url, + client: &http.Client{ + Timeout: timeout, + }, + } +} + +func (provider *Provider) GetInstallTemplate(templateName string) (string, error) { + url := fmt.Sprintf("%s/%v", provider.url, templateName) + response, err := utils.Get(url, provider.client) + if err != nil { + return "", err + } + + defer response.Body.Close() + + installTemplate, err := ioutil.ReadAll(response.Body) + if err != nil { + return "", err + } + + return string(installTemplate), nil +} diff --git a/cli/cmd/common.go b/cli/cmd/common.go index b822097d5..efbab94f1 100644 --- a/cli/cmd/common.go +++ b/cli/cmd/common.go @@ -36,8 +36,8 @@ func startProxyReportErrorIfAny(kubernetesProvider *kubernetes.Provider, ctx con return } - apiProvider = apiserver.NewProvider(GetApiServerUrl(port), apiserver.DefaultRetries, apiserver.DefaultTimeout) - if err := apiProvider.TestConnection(); err != nil { + provider := apiserver.NewProvider(GetApiServerUrl(port), apiserver.DefaultRetries, apiserver.DefaultTimeout) + if err := provider.TestConnection(); err != nil { logger.Log.Debugf("Couldn't connect using proxy, stopping proxy and trying to create port-forward") if err := httpServer.Shutdown(ctx); err != nil { logger.Log.Debugf("Error occurred while stopping proxy %v", errormessage.FormatError(err)) @@ -51,8 +51,8 @@ func startProxyReportErrorIfAny(kubernetesProvider *kubernetes.Provider, ctx con return } - apiProvider = apiserver.NewProvider(GetApiServerUrl(port), apiserver.DefaultRetries, apiserver.DefaultTimeout) - if err := apiProvider.TestConnection(); err != nil { + provider = apiserver.NewProvider(GetApiServerUrl(port), apiserver.DefaultRetries, apiserver.DefaultTimeout) + if err := provider.TestConnection(); err != nil { logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Couldn't connect to API server, for more info check logs at %s", fsUtils.GetLogFilePath())) cancel() return diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 13d2669cc..7ac5ebe2a 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/spf13/cobra" "github.com/up9inc/mizu/cli/telemetry" - "github.com/up9inc/mizu/shared/logger" ) var installCmd = &cobra.Command{ @@ -11,14 +10,7 @@ var installCmd = &cobra.Command{ Short: "Installs mizu components", RunE: func(cmd *cobra.Command, args []string) error { go telemetry.ReportRun("install", nil) - logger.Log.Infof("This command has been deprecated, please use helm as described below.\n\n") - - logger.Log.Infof("To install stable build of Mizu on your cluster using helm, run the following command:") - logger.Log.Infof(" helm install mizu up9mizu --repo https://static.up9.com/mizu/helm --namespace=mizu --create-namespace\n\n") - - logger.Log.Infof("To install development build of Mizu on your cluster using helm, run the following command:") - logger.Log.Infof(" helm install mizu up9mizu --repo https://static.up9.com/mizu/helm-develop --namespace=mizu --create-namespace\n") - + runMizuInstall() return nil }, } diff --git a/cli/cmd/installRunner.go b/cli/cmd/installRunner.go new file mode 100644 index 000000000..4d3877cb2 --- /dev/null +++ b/cli/cmd/installRunner.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "fmt" + "github.com/up9inc/mizu/cli/bucket" + "github.com/up9inc/mizu/cli/config" + "github.com/up9inc/mizu/shared/logger" +) + +func runMizuInstall() { + bucketProvider := bucket.NewProvider(config.Config.Install.TemplateUrl, bucket.DefaultTimeout) + installTemplate, err := bucketProvider.GetInstallTemplate(config.Config.Install.TemplateName) + if err != nil { + logger.Log.Errorf("Failed getting install template, err: %v", err) + return + } + + fmt.Print(installTemplate) +} diff --git a/cli/config/configStruct.go b/cli/config/configStruct.go index 8ef12f5b6..e91829e48 100644 --- a/cli/config/configStruct.go +++ b/cli/config/configStruct.go @@ -23,6 +23,7 @@ const ( type ConfigStruct struct { Tap configStructs.TapConfig `yaml:"tap"` Check configStructs.CheckConfig `yaml:"check"` + Install configStructs.InstallConfig `yaml:"install"` Version configStructs.VersionConfig `yaml:"version"` View configStructs.ViewConfig `yaml:"view"` Logs configStructs.LogsConfig `yaml:"logs"` diff --git a/cli/config/configStructs/installConfig.go b/cli/config/configStructs/installConfig.go new file mode 100644 index 000000000..aac6de81a --- /dev/null +++ b/cli/config/configStructs/installConfig.go @@ -0,0 +1,6 @@ +package configStructs + +type InstallConfig struct { + TemplateUrl string `yaml:"template-url" default:"https://storage.googleapis.com/static.up9.io/mizu/helm-template"` + TemplateName string `yaml:"template-name" default:"helm-template.yaml"` +} diff --git a/cli/utils/httpUtils.go b/cli/utils/httpUtils.go new file mode 100644 index 000000000..0d35f8899 --- /dev/null +++ b/cli/utils/httpUtils.go @@ -0,0 +1,47 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" +) + +// Get - When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +func Get(url string, client *http.Client) (*http.Response, error) { + return checkError(client.Get(url)) +} + +// Post - When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +func Post(url, contentType string, body io.Reader, client *http.Client) (*http.Response, error) { + return checkError(client.Post(url, contentType, body)) +} + +// Do - When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +func Do(req *http.Request, client *http.Client) (*http.Response, error) { + return checkError(client.Do(req)) +} + +func checkError(response *http.Response, errInOperation error) (*http.Response, error) { + if errInOperation != nil { + return response, errInOperation + // Check only if status != 200 (and not status >= 300). Agent APIs return only 200 on success. + } else if response.StatusCode != http.StatusOK { + body, err := ioutil.ReadAll(response.Body) + response.Body.Close() + response.Body = io.NopCloser(bytes.NewBuffer(body)) // rewind + if err != nil { + return response, err + } + + errorMsg := strings.ReplaceAll(string(body), "\n", ";") + return response, fmt.Errorf("got response with status code: %d, body: %s", response.StatusCode, errorMsg) + } + + return response, nil +} diff --git a/shared/kubernetes/mizuTapperSyncer.go b/shared/kubernetes/mizuTapperSyncer.go index 50e6f1250..0b71e6ab6 100644 --- a/shared/kubernetes/mizuTapperSyncer.go +++ b/shared/kubernetes/mizuTapperSyncer.go @@ -325,15 +325,22 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error { tapperSyncer.config.MizuApiFilteringOptions, tapperSyncer.config.LogLevel, tapperSyncer.config.ServiceMesh, - tapperSyncer.config.Tls, - ); err != nil { + tapperSyncer.config.Tls); err != nil { return err } + logger.Log.Debugf("Successfully created %v tappers", len(tapperSyncer.nodeToTappedPodMap)) } else { - if err := tapperSyncer.kubernetesProvider.RemoveDaemonSet(tapperSyncer.context, tapperSyncer.config.MizuResourcesNamespace, TapperDaemonSetName); err != nil { + if err := tapperSyncer.kubernetesProvider.ResetMizuTapperDaemonSet( + tapperSyncer.context, + tapperSyncer.config.MizuResourcesNamespace, + TapperDaemonSetName, + tapperSyncer.config.AgentImage, + TapperPodName); err != nil { return err } + + logger.Log.Debugf("Successfully reset tapper daemon set") } return nil diff --git a/shared/kubernetes/provider.go b/shared/kubernetes/provider.go index 764e6bc68..f6931f4b8 100644 --- a/shared/kubernetes/provider.go +++ b/shared/kubernetes/provider.go @@ -449,9 +449,9 @@ func (provider *Provider) CanI(ctx context.Context, namespace string, resource s Spec: auth.SelfSubjectAccessReviewSpec{ ResourceAttributes: &auth.ResourceAttributes{ Namespace: namespace, - Resource: resource, - Verb: verb, - Group: group, + Resource: resource, + Verb: verb, + Group: group, }, }, } @@ -995,6 +995,55 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac return err } +func (provider *Provider) ResetMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string) error { + agentContainer := applyconfcore.Container() + agentContainer.WithName(tapperPodName) + agentContainer.WithImage(podImage) + + nodeSelectorRequirement := applyconfcore.NodeSelectorRequirement() + nodeSelectorRequirement.WithKey("mizu-non-existing-label") + nodeSelectorRequirement.WithOperator(core.NodeSelectorOpExists) + nodeSelectorTerm := applyconfcore.NodeSelectorTerm() + nodeSelectorTerm.WithMatchExpressions(nodeSelectorRequirement) + nodeSelector := applyconfcore.NodeSelector() + nodeSelector.WithNodeSelectorTerms(nodeSelectorTerm) + nodeAffinity := applyconfcore.NodeAffinity() + nodeAffinity.WithRequiredDuringSchedulingIgnoredDuringExecution(nodeSelector) + affinity := applyconfcore.Affinity() + affinity.WithNodeAffinity(nodeAffinity) + + podSpec := applyconfcore.PodSpec() + podSpec.WithContainers(agentContainer) + podSpec.WithAffinity(affinity) + + podTemplate := applyconfcore.PodTemplateSpec() + podTemplate.WithLabels(map[string]string{ + "app": tapperPodName, + LabelManagedBy: provider.managedBy, + LabelCreatedBy: provider.createdBy, + }) + podTemplate.WithSpec(podSpec) + + labelSelector := applyconfmeta.LabelSelector() + labelSelector.WithMatchLabels(map[string]string{"app": tapperPodName}) + + applyOptions := metav1.ApplyOptions{ + Force: true, + FieldManager: fieldManagerName, + } + + daemonSet := applyconfapp.DaemonSet(daemonSetName, namespace) + daemonSet. + WithLabels(map[string]string{ + LabelManagedBy: provider.managedBy, + LabelCreatedBy: provider.createdBy, + }). + WithSpec(applyconfapp.DaemonSetSpec().WithSelector(labelSelector).WithTemplate(podTemplate)) + + _, err := provider.clientSet.AppsV1().DaemonSets(namespace).Apply(ctx, daemonSet, applyOptions) + return err +} + func (provider *Provider) listPodsImpl(ctx context.Context, regex *regexp.Regexp, namespaces []string, listOptions metav1.ListOptions) ([]core.Pod, error) { var pods []core.Pod for _, namespace := range namespaces { @@ -1038,7 +1087,7 @@ func (provider *Provider) ListAllRunningPodsMatchingRegex(ctx context.Context, r return matchingPods, nil } -func(provider *Provider) ListPodsByAppLabel(ctx context.Context, namespaces string, labelName string) ([]core.Pod, error) { +func (provider *Provider) ListPodsByAppLabel(ctx context.Context, namespaces string, labelName string) ([]core.Pod, error) { pods, err := provider.clientSet.CoreV1().Pods(namespaces).List(ctx, metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", labelName)}) if err != nil { return nil, err