Skip to content

Commit c88a42a

Browse files
committed
Refactor previewctl install-context
1 parent 44de257 commit c88a42a

26 files changed

+1412
-315
lines changed

dev/preview/previewctl/cmd/credentials.go

Lines changed: 65 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,151 +6,127 @@ package cmd
66

77
import (
88
"context"
9-
"fmt"
9+
"os"
10+
"path/filepath"
1011

1112
"github.com/cockroachdb/errors"
13+
kctx "github.com/gitpod-io/gitpod/previewctl/pkg/k8s/context"
14+
"github.com/gitpod-io/gitpod/previewctl/pkg/k8s/context/gke"
15+
"github.com/gitpod-io/gitpod/previewctl/pkg/k8s/context/harvester"
1216
"github.com/sirupsen/logrus"
1317
"github.com/spf13/cobra"
1418
"k8s.io/client-go/tools/clientcmd"
1519
"k8s.io/client-go/tools/clientcmd/api"
20+
"k8s.io/client-go/util/homedir"
1621

17-
"github.com/gitpod-io/gitpod/previewctl/pkg/gcloud"
1822
kube "github.com/gitpod-io/gitpod/previewctl/pkg/k8s"
1923
)
2024

2125
var (
22-
serviceAccountPath string
23-
kubeConfigSavePath string
26+
DefaultKubeConfigPath = filepath.Join(homedir.HomeDir(), clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName)
2427
)
2528

2629
const (
27-
coreDevClusterName = "core-dev"
28-
coreDevProjectID = "gitpod-core-dev"
29-
coreDevClusterZone = "europe-west1-b"
30-
coreDevDesiredContextName = "dev"
30+
coreDevClusterName = "core-dev"
31+
coreDevProjectID = "gitpod-core-dev"
32+
coreDevClusterZone = "europe-west1-b"
3133
)
3234

3335
type getCredentialsOpts struct {
34-
gcpClient *gcloud.Config
35-
logger *logrus.Logger
36+
logger *logrus.Logger
3637

37-
getCredentialsMap map[string]func(ctx context.Context) (*api.Config, error)
38-
configMap map[string]*api.Config
38+
serviceAccountPath string
39+
kubeConfigSavePath string
3940
}
4041

4142
func newGetCredentialsCommand(logger *logrus.Logger) *cobra.Command {
42-
var err error
43-
var client *gcloud.Config
4443
ctx := context.Background()
4544
opts := &getCredentialsOpts{
46-
logger: logger,
47-
configMap: map[string]*api.Config{},
45+
logger: logger,
4846
}
4947

48+
setDefaultKubeConfigPath()
49+
5050
cmd := &cobra.Command{
5151
Use: "get-credentials",
5252
Long: `previewctl get-credentials retrieves the kubernetes configs for core-dev and harvester clusters,
5353
merges them with the default config, and outputs them either to stdout or to a file.`,
54-
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
55-
client, err = gcloud.New(ctx, serviceAccountPath)
54+
RunE: func(cmd *cobra.Command, args []string) error {
55+
configs, err := opts.getCredentials(ctx)
5656
if err != nil {
5757
return err
5858
}
5959

60-
opts.gcpClient = client
61-
opts.getCredentialsMap = map[string]func(ctx context.Context) (*api.Config, error){
62-
"dev": opts.getCoreDevKubeConfig,
63-
"harvester": opts.getHarvesterKubeConfig,
64-
}
65-
66-
return nil
67-
},
68-
RunE: func(cmd *cobra.Command, args []string) error {
69-
for _, kc := range []string{coreDevDesiredContextName, "harvester"} {
70-
if ok := hasAccess(logger, kc); !ok {
71-
config, err := opts.getCredentialsMap[kc](ctx)
72-
if err != nil {
73-
return err
74-
}
75-
76-
opts.configMap[kc] = config
77-
}
78-
}
79-
80-
return opts.mergeContexts()
60+
return kube.OutputContext(opts.kubeConfigSavePath, configs)
8161
},
8262
}
8363

84-
cmd.PersistentFlags().StringVar(&serviceAccountPath, "gcp-service-account", "", "path to the GCP service account to use")
85-
cmd.PersistentFlags().StringVar(&kubeConfigSavePath, "kube-save-path", "", "path to save the generated kubeconfig to")
64+
cmd.PersistentFlags().StringVar(&opts.serviceAccountPath, "gcp-service-account", "", "path to the GCP service account to use")
65+
cmd.PersistentFlags().StringVar(&opts.kubeConfigSavePath, "kube-save-path", DefaultKubeConfigPath, "path to save the generated kubeconfig to")
8666

8767
return cmd
8868
}
8969

90-
func hasAccess(logger *logrus.Logger, contextName string) bool {
91-
config, err := kube.NewFromDefaultConfigWithContext(logger, contextName)
92-
if err != nil {
93-
if errors.Is(err, kube.ErrContextNotExists) {
94-
return false
95-
}
96-
97-
logger.Fatal(err)
98-
}
99-
100-
return config.HasAccess()
101-
}
102-
103-
func (o *getCredentialsOpts) mergeContexts() error {
104-
var err error
105-
configs := make([]*api.Config, 0, len(o.configMap))
106-
107-
for _, config := range o.configMap {
108-
configs = append(configs, config)
109-
}
70+
func (o *getCredentialsOpts) getCredentials(ctx context.Context) (*api.Config, error) {
71+
gkeLoader, err := gke.New(ctx, gke.ConfigLoaderOpts{
72+
Logger: o.logger,
73+
ServiceAccountPath: o.serviceAccountPath,
74+
Name: coreDevClusterName,
75+
ProjectID: coreDevProjectID,
76+
Zone: coreDevClusterZone,
77+
RenamedContextName: gke.DevContextName,
78+
})
11079

111-
finalConfig, err := kube.MergeWithDefaultConfig(configs...)
11280
if err != nil {
113-
return err
81+
return nil, errors.Wrap(err, "failed to instantiate gke loader")
11482
}
11583

116-
if kubeConfigSavePath != "" {
117-
return clientcmd.WriteToFile(*finalConfig, kubeConfigSavePath)
84+
loaderMap := map[string]kctx.Loader{
85+
gke.DevContextName: gkeLoader,
86+
harvester.ContextName: &harvester.ConfigLoader{},
11887
}
11988

120-
bytes, err := clientcmd.Write(*finalConfig)
121-
if err != nil {
122-
return err
123-
}
89+
for _, contextName := range []string{gke.DevContextName, harvester.ContextName} {
90+
loader := loaderMap[contextName]
91+
if kc, err := kube.NewFromDefaultConfigWithContext(o.logger, contextName); err == nil && kc.HasAccess(ctx) {
92+
continue
93+
}
12494

125-
fmt.Println(string(bytes))
95+
kc, err := loader.Load(ctx)
96+
if err != nil {
97+
return nil, err
98+
}
12699

127-
return err
128-
}
100+
configs, err := kube.MergeContextsWithDefault(kc)
101+
if err != nil {
102+
return nil, err
103+
}
129104

130-
func (o *getCredentialsOpts) getCoreDevKubeConfig(ctx context.Context) (*api.Config, error) {
131-
coreDevConfig, err := o.gcpClient.GenerateConfig(ctx, coreDevClusterName, coreDevProjectID, coreDevClusterZone, coreDevDesiredContextName)
132-
if err != nil {
133-
return nil, err
105+
// always save the context at the default path
106+
err = kube.OutputContext(DefaultKubeConfigPath, configs)
107+
if err != nil {
108+
return nil, err
109+
}
134110
}
135111

136-
return coreDevConfig, nil
112+
return kube.MergeContextsWithDefault()
137113
}
138114

139-
func (o *getCredentialsOpts) getHarvesterKubeConfig(ctx context.Context) (*api.Config, error) {
140-
coreDevClientConfig, err := clientcmd.NewNonInteractiveClientConfig(*o.configMap[coreDevDesiredContextName], coreDevDesiredContextName, nil, nil).ClientConfig()
115+
func hasAccess(ctx context.Context, logger *logrus.Logger, contextName string) bool {
116+
config, err := kube.NewFromDefaultConfigWithContext(logger, contextName)
141117
if err != nil {
142-
return nil, err
143-
}
118+
if errors.Is(err, kube.ErrContextNotExists) {
119+
return false
120+
}
144121

145-
kubeConfig, err := kube.NewWithConfig(o.logger, coreDevClientConfig)
146-
if err != nil {
147-
return nil, err
122+
logger.Fatal(err)
148123
}
149124

150-
harvesterConfig, err := kubeConfig.GetHarvesterKubeConfig(ctx)
151-
if err != nil {
152-
return nil, err
153-
}
125+
return config.HasAccess(ctx)
126+
}
154127

155-
return harvesterConfig, nil
128+
func setDefaultKubeConfigPath() {
129+
if v := os.Getenv("KUBECONFIG"); v != "" {
130+
DefaultKubeConfigPath = v
131+
}
156132
}

dev/preview/previewctl/cmd/install_context.go

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,58 @@
55
package cmd
66

77
import (
8+
"context"
9+
"fmt"
10+
"io/fs"
11+
"os"
812
"time"
913

14+
"github.com/cockroachdb/errors"
1015
"github.com/sirupsen/logrus"
1116
"github.com/spf13/cobra"
17+
"k8s.io/client-go/util/homedir"
1218

19+
kube "github.com/gitpod-io/gitpod/previewctl/pkg/k8s"
1320
"github.com/gitpod-io/gitpod/previewctl/pkg/preview"
1421
)
1522

16-
var (
17-
watch bool
18-
timeout time.Duration
19-
)
23+
type installContextCmdOpts struct {
24+
logger *logrus.Logger
25+
26+
watch bool
27+
timeout time.Duration
28+
kubeConfigSavePath string
29+
sshPrivateKeyPath string
30+
31+
getCredentialsOpts *getCredentialsOpts
32+
}
33+
34+
func newInstallContextCmd(logger *logrus.Logger) *cobra.Command {
35+
ctx := context.Background()
36+
37+
opts := installContextCmdOpts{
38+
logger: logger,
39+
getCredentialsOpts: &getCredentialsOpts{
40+
logger: logger,
41+
},
42+
}
2043

21-
func installContextCmd(logger *logrus.Logger) *cobra.Command {
44+
setDefaultKubeConfigPath()
2245

2346
// Used to ensure that we only install contexts
2447
var lastSuccessfulPreviewEnvironment *preview.Preview = nil
2548

2649
install := func(timeout time.Duration) error {
50+
name, err := preview.GetName(branch)
51+
if err != nil {
52+
return err
53+
}
54+
55+
if hasAccess(ctx, logger, name) {
56+
opts.logger.Debugf("Access to [%s] already configured", name)
57+
return nil
58+
}
59+
2760
p, err := preview.New(branch, logger)
2861

2962
if err != nil {
@@ -35,19 +68,42 @@ func installContextCmd(logger *logrus.Logger) *cobra.Command {
3568
return nil
3669
}
3770

38-
err = p.InstallContext(true, timeout)
71+
err = p.InstallContext(ctx, preview.InstallCtxOpts{
72+
Wait: opts.watch,
73+
Timeout: opts.timeout,
74+
KubeSavePath: opts.kubeConfigSavePath,
75+
SSHPrivateKeyPath: opts.sshPrivateKeyPath,
76+
})
77+
3978
if err == nil {
4079
lastSuccessfulPreviewEnvironment = p
4180
}
81+
4282
return err
4383
}
4484

4585
cmd := &cobra.Command{
4686
Use: "install-context",
4787
Short: "Installs the kubectl context of a preview environment.",
48-
RunE: func(cmd *cobra.Command, args []string) error {
88+
PreRunE: func(cmd *cobra.Command, args []string) error {
89+
configs, err := opts.getCredentialsOpts.getCredentials(ctx)
90+
if err != nil {
91+
return err
92+
}
93+
94+
err = kube.OutputContext(opts.kubeConfigSavePath, configs)
95+
if err != nil {
96+
return err
97+
}
4998

50-
if watch {
99+
if _, err = os.Stat(opts.sshPrivateKeyPath); errors.Is(err, fs.ErrNotExist) {
100+
return preview.InstallVMSSHKeys()
101+
}
102+
103+
return nil
104+
},
105+
RunE: func(cmd *cobra.Command, args []string) error {
106+
if opts.watch {
51107
for range time.Tick(15 * time.Second) {
52108
// We're using a short timeout here to handle the scenario where someone switches
53109
// to a branch that doens't have a preview envrionment. In that case the default
@@ -59,14 +115,18 @@ func installContextCmd(logger *logrus.Logger) *cobra.Command {
59115
}
60116
}
61117
} else {
62-
return install(timeout)
118+
return install(opts.timeout)
63119
}
64120

65121
return nil
66122
},
67123
}
68124

69-
cmd.Flags().BoolVar(&watch, "watch", false, "If watch is enabled, previewctl will keep trying to install the kube-context every 15 seconds.")
70-
cmd.Flags().DurationVarP(&timeout, "timeout", "t", 10*time.Minute, "Timeout before considering the installation failed")
125+
cmd.Flags().BoolVar(&opts.watch, "watch", false, "If watch is enabled, previewctl will keep trying to install the kube-context every 15 seconds.")
126+
cmd.Flags().DurationVarP(&opts.timeout, "timeout", "t", 10*time.Minute, "Timeout before considering the installation failed")
127+
cmd.PersistentFlags().StringVar(&opts.kubeConfigSavePath, "kube-save-path", DefaultKubeConfigPath, "path to save the generated kubeconfig to")
128+
cmd.PersistentFlags().StringVar(&opts.sshPrivateKeyPath, "private-key-path", fmt.Sprintf("%s/.ssh/vm_id_rsa", homedir.HomeDir()), "path to the private key used to authenticate with the VM")
129+
cmd.PersistentFlags().StringVar(&opts.getCredentialsOpts.serviceAccountPath, "gcp-service-account", "", "path to the GCP service account to use")
130+
71131
return cmd
72132
}

dev/preview/previewctl/cmd/list_previews.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package cmd
66

77
import (
8+
"context"
9+
810
"github.com/sirupsen/logrus"
911
"github.com/spf13/cobra"
1012

@@ -26,7 +28,7 @@ func listPreviewsCmd(logger *logrus.Logger) *cobra.Command {
2628
return err
2729
}
2830

29-
err = p.ListAllPreviews()
31+
err = p.ListAllPreviews(context.Background())
3032
if err != nil {
3133
logger.WithFields(logrus.Fields{"err": err}).Fatal("Failed to list previews.")
3234
}

0 commit comments

Comments
 (0)