From 87b40f744affbbdc862ef2e2676bd5e643f4074b Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Wed, 11 Jan 2023 14:26:21 -0800 Subject: [PATCH] rollouts: added skeleton CLI (#3724) * rollouts: added skeleton CLI * added table display --- rollouts/Makefile | 5 ++ rollouts/cli/get/get.go | 66 ++++++++++++++++++++++ rollouts/cli/main.go | 53 ++++++++++++++++++ rollouts/cli/status/status.go | 86 +++++++++++++++++++++++++++++ rollouts/go.mod | 3 + rollouts/go.sum | 8 +++ rollouts/rolloutsclient/client.go | 92 +++++++++++++++++++++++++++++++ 7 files changed, 313 insertions(+) create mode 100644 rollouts/cli/get/get.go create mode 100644 rollouts/cli/main.go create mode 100644 rollouts/cli/status/status.go create mode 100644 rollouts/rolloutsclient/client.go diff --git a/rollouts/Makefile b/rollouts/Makefile index 2cc041386e..b1f1bdc474 100644 --- a/rollouts/Makefile +++ b/rollouts/Makefile @@ -82,6 +82,11 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go + +.PHONY: cli +cli: + go build -o ./bin/rollouts ./cli + # If you wish built the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ diff --git a/rollouts/cli/get/get.go b/rollouts/cli/get/get.go new file mode 100644 index 0000000000..d6cfd0253e --- /dev/null +++ b/rollouts/cli/get/get.go @@ -0,0 +1,66 @@ +// Copyright 2023 Google LLC +// +// 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 get + +import ( + "context" + "fmt" + + "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/spf13/cobra" +) + +func NewCommand(ctx context.Context) *cobra.Command { + return newRunner(ctx).Command +} + +func newRunner(ctx context.Context) *runner { + r := &runner{ + ctx: ctx, + } + c := &cobra.Command{ + Use: "get", + Short: "lists rollouts", + Long: "lists rollouts", + Example: "lists rollouts", + RunE: r.runE, + } + r.Command = c + return r +} + +type runner struct { + ctx context.Context + Command *cobra.Command +} + +func (r *runner) runE(cmd *cobra.Command, args []string) error { + rlc, err := rolloutsclient.New() + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + rollouts, err := rlc.List(r.ctx, "") + if err != nil { + fmt.Printf("%s\n", err) + return err + } + for _, rollout := range rollouts.Items { + fmt.Printf("%s\n", rollout.Name) + } + + return nil +} diff --git a/rollouts/cli/main.go b/rollouts/cli/main.go new file mode 100644 index 0000000000..b6eff2e499 --- /dev/null +++ b/rollouts/cli/main.go @@ -0,0 +1,53 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "os" + + "github.com/GoogleContainerTools/kpt/rollouts/cli/get" + "github.com/GoogleContainerTools/kpt/rollouts/cli/status" + "github.com/spf13/cobra" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +func main() { + ctx := context.Background() + + rolloutsCmd := &cobra.Command{ + Use: "cli", + Short: "cli ", + Long: "cli", + RunE: func(cmd *cobra.Command, args []string) error { + h, err := cmd.Flags().GetBool("help") + if err != nil { + return err + } + if h { + return cmd.Help() + } + return cmd.Usage() + }, + } + + rolloutsCmd.AddCommand( + get.NewCommand(ctx), + status.NewCommand(ctx), + ) + if err := rolloutsCmd.Execute(); err != nil { + os.Exit(-1) + } +} diff --git a/rollouts/cli/status/status.go b/rollouts/cli/status/status.go new file mode 100644 index 0000000000..0d8b88f553 --- /dev/null +++ b/rollouts/cli/status/status.go @@ -0,0 +1,86 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package status + +import ( + "context" + "fmt" + + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/spf13/cobra" + + "github.com/jedib0t/go-pretty/v6/table" +) + +func newRunner(ctx context.Context) *runner { + r := &runner{ + ctx: ctx, + } + c := &cobra.Command{ + Use: "status", + Short: "displays status of a rollout", + Long: "displays status of a rollout", + Example: "displays status of a rollout", + RunE: r.runE, + } + r.Command = c + return r +} + +func NewCommand(ctx context.Context) *cobra.Command { + return newRunner(ctx).Command +} + +type runner struct { + ctx context.Context + Command *cobra.Command +} + +func (r *runner) runE(cmd *cobra.Command, args []string) error { + rlc, err := rolloutsclient.New() + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + if len(args) == 0 { + fmt.Printf("must provide rollout name") + return nil + } + + rollout, err := rlc.Get(r.ctx, args[0]) + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + renderStatusAsTable(cmd, rollout) + return nil +} + +func renderStatusAsTable(cmd *cobra.Command, rollout *rolloutsapi.Rollout) { + t := table.NewWriter() + t.SetOutputMirror(cmd.OutOrStdout()) + t.AppendHeader(table.Row{"CLUSTER", "PACKAGE ID", "PACKAGE STATUS", "SYNC STATUS"}) + for _, cluster := range rollout.Status.ClusterStatuses { + pkgStatus := cluster.PackageStatus + t.AppendRow([]interface{}{cluster.Name, pkgStatus.PackageID, pkgStatus.Status, pkgStatus.SyncStatus}) + } + t.AppendSeparator() + // t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000}) + // t.AppendFooter(table.Row{"", "", "Total", 10000}) + t.Render() +} diff --git a/rollouts/go.mod b/rollouts/go.mod index e424558d19..d76ea5efa1 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -53,9 +53,11 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/jedib0t/go-pretty/v6 v6.4.4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -65,6 +67,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index 69139d8e9c..f470fd5cf7 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -220,6 +220,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc= +github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -249,6 +251,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -275,6 +279,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 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 v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -299,6 +304,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -320,6 +327,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= diff --git a/rollouts/rolloutsclient/client.go b/rollouts/rolloutsclient/client.go new file mode 100644 index 0000000000..5fb6e3171b --- /dev/null +++ b/rollouts/rolloutsclient/client.go @@ -0,0 +1,92 @@ +// Copyright 2023 Google LLC +// +// 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 rolloutsclient + +import ( + "context" + "fmt" + + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + coreapi "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +// Client implments client for the rollouts API. +type Client struct { + client client.Client +} + +func New() (*Client, error) { + scheme, err := createScheme() + if err != nil { + return nil, err + } + cl, err := client.New(config.GetConfigOrDie(), client.Options{ + Scheme: scheme, + }) + if err != nil { + return nil, err + } + return &Client{client: cl}, nil +} + +func createScheme() (*runtime.Scheme, error) { + scheme := runtime.NewScheme() + + for _, api := range (runtime.SchemeBuilder{ + rolloutsapi.AddToScheme, + coreapi.AddToScheme, + metav1.AddMetaToScheme, + }) { + if err := api(scheme); err != nil { + return nil, err + } + } + return scheme, nil +} + +func (rlc *Client) List(ctx context.Context, ns string) (*rolloutsapi.RolloutList, error) { + if ns == "" { + ns = "default" + } + + rollouts := &rolloutsapi.RolloutList{} + if err := rlc.client.List(context.Background(), rollouts, client.InNamespace(ns)); err != nil { + return nil, err + } + + return rollouts, nil +} + +func (rlc *Client) Get(ctx context.Context, name string) (*rolloutsapi.Rollout, error) { + if name == "" { + return nil, fmt.Errorf("must provide rollout name") + } + + key := types.NamespacedName{ + Namespace: "default", + Name: name, + } + rollout := &rolloutsapi.Rollout{} + if err := rlc.client.Get(context.Background(), key, rollout); err != nil { + return nil, err + } + + return rollout, nil +}