Skip to content

Commit

Permalink
feat: checks if operator exists for server-related cmds (#1081)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartoszmajsak authored Sep 16, 2022
1 parent 4750868 commit bdf159e
Show file tree
Hide file tree
Showing 18 changed files with 461 additions and 37 deletions.
8 changes: 4 additions & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ linters-settings:
golint:
min-confidence: 0
gocyclo:
min-complexity: 12
min-complexity: 16
cyclop:
max-complexity: 12
max-complexity: 16
dupl:
threshold: 128
funlen:
lines: 128
statements: 64
goconst:
min-len: 2
min-occurrences: 2
min-len: 4
min-occurrences: 3
depguard:
list-type: blacklist
packages:
Expand Down
3 changes: 2 additions & 1 deletion cmd/ike/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/maistra/istio-workspace/pkg/cmd/serve"
"github.com/maistra/istio-workspace/pkg/cmd/version"
"github.com/maistra/istio-workspace/pkg/hook"
"github.com/maistra/istio-workspace/pkg/k8s"
"github.com/maistra/istio-workspace/pkg/log"
)

Expand All @@ -32,7 +33,7 @@ func main() {
// Setting random seed e.g. for session name generator
rand.Seed(time.Now().UTC().UnixNano())

rootCmd := cmd.NewCmd()
rootCmd := cmd.NewCmd(&k8s.ClusterVerifier{})
rootCmd.AddCommand(
version.NewCmd(),
create.NewCmd(),
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
istio.io/api v0.0.0-20211012192923-310f2a3f3c76
istio.io/client-go v1.11.4
k8s.io/api v0.24.4
k8s.io/apiextensions-apiserver v0.24.2
k8s.io/apimachinery v0.24.4
k8s.io/client-go v0.24.4
k8s.io/code-generator v0.24.4
Expand Down Expand Up @@ -139,8 +140,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
istio.io/gogo-genproto v0.0.0-20210113155706-4daf5697332f // indirect
k8s.io/apiextensions-apiserver v0.24.2 // indirect
k8s.io/component-base v0.24.2 // indirect
k8s.io/component-base v0.24.4 // indirect
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1375,8 +1375,9 @@ k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI
k8s.io/code-generator v0.24.4 h1:HLnoAabkTFKy1Ex4cMvffz6KkWFJ7oFN2yX37Icbbyg=
k8s.io/code-generator v0.24.4/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA=
k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
k8s.io/component-base v0.24.4 h1:WEGRp06GBYVwxp5JdiRaJ1zkdOhrqucxRv/8IrABLG0=
k8s.io/component-base v0.24.4/go.mod h1:sWxkgcMfbYHadw0OJ0N+vIscd14/nqSIM2veCdg843o=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI=
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/config/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/config"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
)

Expand All @@ -27,7 +28,7 @@ var _ = Describe("Usage of ike command configuration", func() {
testCmd = NewTestCmd()
testCmd.SilenceUsage = true
testCmd.SilenceErrors = true
NewCmd().AddCommand(testCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(testCmd)
})

AfterEach(func() {
Expand Down
5 changes: 3 additions & 2 deletions pkg/cmd/create/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/create"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
)

Expand All @@ -18,7 +19,7 @@ var _ = Describe("Usage of ike create command", func() {
createCmd = create.NewCmd()
createCmd.SilenceUsage = true
createCmd.SilenceErrors = true
NewCmd().AddCommand(createCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(createCmd)
})

Describe("input validation", func() {
Expand All @@ -32,7 +33,7 @@ var _ = Describe("Usage of ike create command", func() {
Expect(err.Error()).To(And(ContainSubstring("required flag(s)"), ContainSubstring("deployment")))
})

It("should fail when image command is not specified", func() {
It("should fail when image flag is not specified", func() {
defer TemporaryUnsetEnvVars("IKE_IMAGE")()
_, err := ValidateArgumentsOf(createCmd).Passing("--deployment", "rating-service")

Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/delete/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/delete"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
)

Expand All @@ -18,7 +19,7 @@ var _ = Describe("Usage of ike delete command", func() {
createCmd = delete.NewCmd()
createCmd.SilenceUsage = true
createCmd.SilenceErrors = true
NewCmd().AddCommand(createCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(createCmd)
})

Describe("input validation", func() {
Expand Down
12 changes: 7 additions & 5 deletions pkg/cmd/develop/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/go-logr/logr"
"github.com/spf13/cobra"

"github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/config"
"github.com/maistra/istio-workspace/pkg/cmd/execute"
"github.com/maistra/istio-workspace/pkg/cmd/flag"
Expand All @@ -26,10 +27,11 @@ var (

errorTpNotAvailable = errors.Errorf("unable to find %s on your $PATH", telepresence.BinaryName)

// Used in the tp-wrapper to check if passed command
// can be parsed (so has all required flags).
tpAnnotations = map[string]string{
"telepresence": "translatable",
annotations = map[string]string{
// Used in the tp-wrapper to check if passed command
// can be parsed (so has all required flags).
"telepresence": "translatable",
cmd.AnnotationOperatorRequired: "true",
}
)

Expand All @@ -40,7 +42,7 @@ func NewCmd() *cobra.Command {
Short: "Starts the development flow",
SilenceUsage: true,
TraverseChildren: true,
Annotations: tpAnnotations,
Annotations: annotations,
PreRunE: func(cmd *cobra.Command, args []string) error {
if !telepresence.BinaryAvailable() {
return errorTpNotAvailable
Expand Down
5 changes: 3 additions & 2 deletions pkg/cmd/develop/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/develop"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
"github.com/maistra/istio-workspace/test/shell"
)
Expand All @@ -19,11 +20,11 @@ var _ = Describe("Usage of ike develop command", func() {

var developCmd *cobra.Command

BeforeEach(func() {
JustBeforeEach(func() {
developCmd = develop.NewCmd()
developCmd.SilenceUsage = true
developCmd.SilenceErrors = true
NewCmd().AddCommand(developCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(developCmd)
})

Context("checking telepresence binary existence", func() {
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/execute/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/execute"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
"github.com/maistra/istio-workspace/test/shell"
)
Expand All @@ -27,7 +28,7 @@ var _ = Describe("Usage of ike execute command", func() {
executeCmd.SilenceErrors = false
executeCmd.Annotations = make(map[string]string, 1)
executeCmd.Annotations["test"] = "true"
NewCmd().AddCommand(executeCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(executeCmd)
})

AfterEach(func() {
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/flag/limited_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

. "github.com/maistra/istio-workspace/pkg/cmd"
"github.com/maistra/istio-workspace/pkg/cmd/flag"
"github.com/maistra/istio-workspace/pkg/k8s"
. "github.com/maistra/istio-workspace/test"
)

Expand All @@ -20,7 +21,7 @@ var _ = Describe("Usage of limited flags", func() {
testCmd = newTestCmd("stout", "s", "ale", "a", "kolsch", "k")
testCmd.SilenceUsage = true
testCmd.SilenceErrors = true
NewCmd().AddCommand(testCmd)
NewCmd(&k8s.AssumeOperatorInstalled{}).AddCommand(testCmd)
})

It("should accept defined value using full name", func() {
Expand Down
56 changes: 41 additions & 15 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,51 @@ import (
"github.com/maistra/istio-workspace/pkg/cmd/format"
"github.com/maistra/istio-workspace/pkg/cmd/version"
"github.com/maistra/istio-workspace/pkg/hook"
"github.com/maistra/istio-workspace/pkg/k8s"
"github.com/maistra/istio-workspace/pkg/log"
v "github.com/maistra/istio-workspace/version"
)

const (
AnnotationOperatorRequired = "operator-required"

DocsLink = "https://istio-workspace-docs.netlify.com"
)

var logger = func() logr.Logger {
return log.Log.WithValues("type", "root")
}

// NewCmd creates instance of root "ike" Cobra Command with flags and execution logic defined.
func NewCmd() *cobra.Command {
func NewCmd(verifier k8s.InstallationVerifier) *cobra.Command {
var configFile string
releaseInfo := make(chan string, 1)

released := v.Released()

rootCmd := &cobra.Command{
SilenceErrors: true,
Use: "ike",
Short: "ike lets you safely develop and test on production without a sweat!\n\n" +
"For detailed documentation please visit https://istio-workspace-docs.netlify.com/\n\n",
Short: `ike lets you safely develop and test on production without a sweat!
For detailed documentation please visit ` + DocsLink,
BashCompletionFunction: completion.BashCompletionFunc,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
var collectedErrors error

if err := config.SetupConfigSources(loadConfigFileName(cmd)); err != nil {
collectedErrors = errors.Append(collectedErrors, errors.Wrapf(err, "failed setting config sources"))
}

if released {
go func() {
latestRelease, _ := version.LatestRelease()
if !version.IsLatestRelease(latestRelease) {
releaseInfo <- fmt.Sprintf("WARN: you are using %s which is not the latest release (newest is %s).\n"+
"Follow release notes for update info https://github.com/Maistra/istio-workspace/releases/latest", v.Version, latestRelease)
} else {
releaseInfo <- ""
}
}()
go checkIfLatestRelease(releaseInfo)
}

if cmd.Annotations[AnnotationOperatorRequired] == "true" {
crdExists, err := verifier.CheckCRD()
if !crdExists {
return errors.Wrapf(err, "failed to locate istio-operator on your cluster, "+
"please follow installation instructions %s/istio-workspace/%s/getting_started.html#_installing_cluster_component\n", DocsLink, v.CurrentVersion())
}
}

hook.Listen()
Expand All @@ -57,7 +71,7 @@ func NewCmd() *cobra.Command {
}
})

return errors.Wrap(config.SetupConfigSources(loadConfigFileName(cmd)), "failed setting config sources")
return errors.Wrap(collectedErrors, "failed setting up command")
},
RunE: func(cmd *cobra.Command, args []string) error {
shouldPrintVersion, _ := cmd.Flags().GetBool("version")
Expand All @@ -71,7 +85,9 @@ func NewCmd() *cobra.Command {
},
PostRunE: func(cmd *cobra.Command, args []string) error {
defer func() {
close(releaseInfo)
if releaseInfo != nil {
close(releaseInfo)
}
}()
if released {
timer := time.NewTimer(2 * time.Second)
Expand Down Expand Up @@ -106,6 +122,16 @@ func NewCmd() *cobra.Command {
return rootCmd
}

func checkIfLatestRelease(releaseInfo chan<- string) {
latestRelease, _ := version.LatestRelease()
if !version.IsLatestRelease(latestRelease) {
releaseInfo <- fmt.Sprintf("WARN: you are using %s which is not the latest release (newest is %s).\n"+
"Follow release notes for update info https://github.com/Maistra/istio-workspace/releases/latest", v.Version, latestRelease)
} else {
releaseInfo <- ""
}
}

func loadConfigFileName(cmd *cobra.Command) (configFileName string, defaultConfigSource bool) {
configFlag := cmd.Flag("config")
configFileName = viper.GetString("config")
Expand Down
4 changes: 3 additions & 1 deletion pkg/hook/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func Listen() {
// Close closes underlying channel.
func Close() {
hooks.Lock()
close(hooks.done)
if hooks.done != nil {
close(hooks.done)
}
hooks.Unlock()
}
54 changes: 54 additions & 0 deletions pkg/k8s/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package k8s

import (
"context"

"emperror.dev/errors"
errorsK8s "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/maistra/istio-workspace/pkg/k8s/dynclient"
)

type InstallationVerifier interface {
CheckCRD() (bool, error)
}

var _ InstallationVerifier = &ClusterVerifier{}

type ClusterVerifier struct {
client *dynclient.Client
}

func (v *ClusterVerifier) CheckCRD() (bool, error) {
var err error
if v.client == nil {
v.client, err = dynclient.NewDefaultDynamicClient("", false)
if err != nil {
return false, errors.Wrap(err, "failed creating dynamic client for cluster verification")
}
}

res, err := v.client.Dynamic().Resource(schema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Version: "v1",
Resource: "customresourcedefinitions",
}).Get(context.Background(), "sessions.workspace.maistra.io", metav1.GetOptions{})

if errorsK8s.IsNotFound(err) {
return false, nil
}

return res != nil, errors.Wrap(err, "failed checking if istio-workspace operator is installed")
}

var _ InstallationVerifier = &AssumeOperatorInstalled{}

// AssumeOperatorInstalled is used for testing where we simply assume CRD is always installed.
// This is in particular useful as test double where cluster is not involved at all.
type AssumeOperatorInstalled struct{}

func (AssumeOperatorInstalled) CheckCRD() (bool, error) {
return true, nil
}
Loading

0 comments on commit bdf159e

Please sign in to comment.