Skip to content

Commit e7c0d24

Browse files
committed
Client implementation for using devspace cloud
1 parent 7c0276f commit e7c0d24

6 files changed

Lines changed: 395 additions & 63 deletions

File tree

cmd/init.go

Lines changed: 82 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ import (
99
"strconv"
1010
"strings"
1111

12+
"github.com/covexo/devspace/pkg/util/kubeconfig"
13+
14+
"k8s.io/client-go/tools/clientcmd"
15+
1216
"github.com/covexo/devspace/pkg/devspace/clients/kubectl"
17+
"github.com/covexo/devspace/pkg/devspace/login"
1318

1419
"github.com/covexo/devspace/pkg/devspace/builder/docker"
1520

1621
"github.com/covexo/devspace/pkg/devspace/config/configutil"
1722
"github.com/covexo/devspace/pkg/devspace/generator"
1823
"github.com/covexo/devspace/pkg/util/log"
1924
"github.com/covexo/devspace/pkg/util/randutil"
20-
homedir "github.com/mitchellh/go-homedir"
2125

2226
"github.com/covexo/devspace/pkg/devspace/config/v1"
2327
"github.com/covexo/devspace/pkg/util/stdinutil"
@@ -116,6 +120,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) {
116120
} else {
117121
cmd.config = configutil.GetConfigInstance()
118122
}
123+
119124
configutil.Merge(cmd.config, &v1.Config{
120125
Version: configutil.String("v1"),
121126
DevSpace: &v1.DevSpaceConfig{
@@ -217,7 +222,6 @@ func (cmd *InitCmd) initChartGenerator() {
217222

218223
func (cmd *InitCmd) configureDevSpace() {
219224
_, chartDirNotFound := os.Stat(cmd.workdir + "/chart")
220-
221225
if chartDirNotFound == nil {
222226
/*TODO
223227
existingChartYaml := map[interface{}]interface{}{}
@@ -239,18 +243,13 @@ func (cmd *InitCmd) configureDevSpace() {
239243
}
240244
}*/
241245
}
246+
242247
cmd.config.DevSpace.Release.Name = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
243248
Question: "What is the name of your application?",
244249
DefaultValue: *cmd.config.DevSpace.Release.Name,
245250
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
246251
})
247252

248-
// cmd.appConfig.Container.Ports, _ = strconv.Atoi(stdinutil.GetFromStdin(&stdinutil.GetFromStdin_params{
249-
// Question: "Which port(s) does your application listen on? (separated by spaces)",
250-
// DefaultValue: strconv.Itoa(cmd.appConfig.Container.Port),
251-
// ValidationRegexPattern: "^[1-9][0-9]{0,4}?(\\s[1-9][0-9]{0,4})?$",
252-
// }))
253-
254253
ports := strings.Split(*stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
255254
Question: "Which port(s) does your application listen on? (separated by spaces)",
256255
DefaultValue: "",
@@ -266,11 +265,13 @@ func (cmd *InitCmd) configureDevSpace() {
266265
}
267266
cmd.addDefaultSyncConfig()
268267

269-
cmd.config.DevSpace.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
270-
Question: "Which Kubernetes namespace should your application run in?",
271-
DefaultValue: *cmd.config.DevSpace.Release.Namespace,
272-
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
273-
})
268+
if cmd.config.DevSpace.Release.Namespace == nil || len(*cmd.config.DevSpace.Release.Namespace) == 0 {
269+
cmd.config.DevSpace.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
270+
Question: "Which Kubernetes namespace should your application run in?",
271+
DefaultValue: *cmd.config.DevSpace.Release.Namespace,
272+
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
273+
})
274+
}
274275

275276
/* TODO
276277
cmd.appConfig.External.Domain = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
@@ -340,38 +341,85 @@ func (cmd *InitCmd) configureTiller() {
340341
tillerConfig := cmd.config.Services.Tiller
341342
tillerRelease := tillerConfig.Release
342343

343-
if tillerRelease.Namespace == nil {
344+
if tillerRelease.Namespace == nil || len(*tillerRelease.Namespace) == 0 {
344345
tillerRelease.Namespace = cmd.config.DevSpace.Release.Namespace
346+
347+
tillerRelease.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
348+
Question: "Which Kubernetes namespace should your tiller server run in?",
349+
DefaultValue: *tillerRelease.Namespace,
350+
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
351+
})
345352
}
346-
tillerRelease.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
347-
Question: "Which Kubernetes namespace should your tiller server run in?",
348-
DefaultValue: *tillerRelease.Namespace,
349-
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
350-
})
353+
}
354+
355+
func (cmd *InitCmd) useDevSpaceCloud() bool {
356+
useDevSpaceCloud := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
357+
Question: "Do you want to use the devspace cloud? (free ready-to-use kubernetes) (yes | no)",
358+
DefaultValue: "yes",
359+
ValidationRegexPattern: "^(yes)|(no)$",
360+
}) == "yes"
361+
362+
if useDevSpaceCloud {
363+
namespace, cluster, authInfo, err := login.CheckAuth()
364+
if err != nil {
365+
log.Fatalf("Error authenticating to devspace cloud: %v", err)
366+
}
367+
368+
cmd.config.DevSpace.Release.Namespace = &namespace
369+
cmd.config.Services.Tiller.Release.Namespace = &namespace
370+
371+
addToContext := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
372+
Question: "Do you want to add the devspace-cloud to the $HOME/.kube/config file? (yes | no)",
373+
DefaultValue: "yes",
374+
ValidationRegexPattern: "^(yes)|(no)$",
375+
}) == "yes"
376+
377+
if addToContext {
378+
err = login.UpdateKubeConfig(cluster, authInfo, true)
379+
if err != nil {
380+
log.Fatalf("Couldn't update kube config: %v", err)
381+
}
382+
383+
cmd.config.Cluster.UseKubeConfig = &addToContext
384+
cmd.config.Cluster.KubeContext = configutil.String(login.DevSpaceCloudContextName)
385+
} else {
386+
cmd.config.Cluster.UseKubeConfig = &addToContext
387+
cmd.config.Cluster.APIServer = &cluster.Server
388+
cmd.config.Cluster.CaCert = configutil.String(string(cluster.CertificateAuthorityData))
389+
390+
cmd.config.Cluster.User = &v1.ClusterUser{
391+
ClientCert: configutil.String(string(authInfo.ClientCertificateData)),
392+
ClientKey: configutil.String(string(authInfo.ClientKeyData)),
393+
}
394+
}
395+
396+
return true
397+
}
398+
399+
return false
351400
}
352401

353402
func (cmd *InitCmd) configureKubernetes() {
354403
clusterConfig := cmd.config.Cluster
355404
useKubeConfig := false
356-
homeDir, homeErr := homedir.Dir()
357405

358-
if homeErr != nil {
359-
log.With(homeErr).Fatalf("Unable to determine home dir")
406+
// Check if devspace cloud should be used
407+
if cmd.useDevSpaceCloud() {
408+
return
360409
}
361-
kubeConfigPath := homeDir + "/.kube/config"
362-
363-
_, kubeConfigNotFound := os.Stat(kubeConfigPath)
364410

365-
if kubeConfigNotFound == nil {
411+
_, err := os.Stat(clientcmd.RecommendedHomeFile)
412+
if err == nil {
366413
skipAnswer := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
367414
Question: "Do you want to use your existing $HOME/.kube/config for Kubernetes access? (yes | no)",
368415
DefaultValue: "yes",
369416
ValidationRegexPattern: "^(yes)|(no)$",
370417
})
418+
371419
useKubeConfig = (*skipAnswer == "yes")
372420
}
373-
clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig)
374421

422+
clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig)
375423
if !useKubeConfig {
376424
if clusterConfig.APIServer == nil {
377425
clusterConfig.APIServer = configutil.String("https://192.168.99.100:8443")
@@ -393,15 +441,10 @@ func (cmd *InitCmd) configureKubernetes() {
393441

394442
if clusterConfig.User == nil {
395443
clusterConfig.User = &v1.ClusterUser{
396-
Username: configutil.String(""),
397444
ClientCert: configutil.String(""),
398445
ClientKey: configutil.String(""),
399446
}
400447
} else {
401-
if clusterConfig.User.Username == nil {
402-
clusterConfig.User.Username = configutil.String("")
403-
}
404-
405448
if clusterConfig.User.ClientCert == nil {
406449
clusterConfig.User.ClientCert = configutil.String("")
407450
}
@@ -410,11 +453,6 @@ func (cmd *InitCmd) configureKubernetes() {
410453
clusterConfig.User.ClientKey = configutil.String("")
411454
}
412455
}
413-
clusterConfig.User.Username = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
414-
Question: "What is your Kubernetes username?",
415-
DefaultValue: *clusterConfig.User.Username,
416-
ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name,
417-
})
418456
clusterConfig.User.ClientCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{
419457
Question: "What is your Kubernetes client certificate? (PEM)",
420458
DefaultValue: *clusterConfig.User.ClientCert,
@@ -425,6 +463,13 @@ func (cmd *InitCmd) configureKubernetes() {
425463
DefaultValue: *clusterConfig.User.ClientKey,
426464
InputTerminationString: "-----END RSA PRIVATE KEY-----",
427465
})
466+
} else {
467+
currentContext, err := kubeconfig.GetCurrentContext()
468+
if err != nil {
469+
log.Fatalf("Couldn't determine current kubernetes context: %v", err)
470+
}
471+
472+
clusterConfig.KubeContext = &currentContext
428473
}
429474
}
430475

docs/docs/configuration/config.yaml.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ The `tiller` service is defined by:
235235
## cluster
236236
The `cluster` field specifies:
237237
- `useKubeConfig` *bool* if true use the credentials defined in $HOME/.kube/config
238+
- `kubeContext` *string* the context to use from $HOME/.kube/config
238239

239240
If `useKubeConfig` is `false`, the following fields need to be specified:
240241
- `apiServer` *string* (Kubernetes API-Server URL)
@@ -243,7 +244,6 @@ If `useKubeConfig` is `false`, the following fields need to be specified:
243244

244245
### cluster.user
245246
ClusterUser:
246-
- `username` *string*
247247
- `clientCert` *string* (PEM format)
248248
- `clientKey` *string* (PEM format)
249249

pkg/devspace/clients/kubectl/client.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"io"
88
"net/http"
99
"os"
10-
"path/filepath"
1110
"sync"
1211

12+
"github.com/covexo/devspace/pkg/util/kubeconfig"
13+
1314
"github.com/covexo/devspace/pkg/devspace/config/configutil"
14-
"github.com/covexo/devspace/pkg/util/fsutil"
1515
"github.com/covexo/devspace/pkg/util/log"
1616
dockerterm "github.com/docker/docker/pkg/term"
1717
k8sv1 "k8s.io/api/core/v1"
@@ -44,6 +44,37 @@ func NewClient() (*kubernetes.Clientset, error) {
4444
return kubernetes.NewForConfig(config)
4545
}
4646

47+
//GetClientConfig loads the configuration for kubernetes clients and parses it to *rest.Config
48+
func GetClientConfig() (*rest.Config, error) {
49+
config := configutil.GetConfig(false)
50+
if config.Cluster == nil {
51+
return nil, errors.New("Couldn't load cluster config, did you run devspace init")
52+
}
53+
54+
if (config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig) || config.Cluster.APIServer == nil {
55+
// If we should use a certain kube context use that
56+
if config.Cluster.KubeContext != nil && len(*config.Cluster.KubeContext) > 0 {
57+
kubeConfig, err := kubeconfig.ReadKubeConfig(clientcmd.RecommendedHomeFile)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return clientcmd.NewNonInteractiveClientConfig(*kubeConfig, *config.Cluster.KubeContext, nil, clientcmd.NewDefaultClientConfigLoadingRules()).ClientConfig()
63+
}
64+
65+
return clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
66+
}
67+
68+
return &rest.Config{
69+
Host: *config.Cluster.APIServer,
70+
TLSClientConfig: rest.TLSClientConfig{
71+
CAData: []byte(*config.Cluster.CaCert),
72+
CertData: []byte(*config.Cluster.User.ClientCert),
73+
KeyData: []byte(*config.Cluster.User.ClientKey),
74+
},
75+
}, nil
76+
}
77+
4778
// IsMinikube returns true if the Kubernetes cluster is a minikube
4879
func IsMinikube() bool {
4980
if isMinikubeVar == nil {
@@ -204,28 +235,6 @@ func GetPodsFromDeployment(kubectl *kubernetes.Clientset, deployment, namespace
204235
})
205236
}
206237

207-
//GetClientConfig loads the configuration for kubernetes clients and parses it to *rest.Config
208-
func GetClientConfig() (*rest.Config, error) {
209-
config := configutil.GetConfig(false)
210-
211-
if config.Cluster == nil {
212-
return nil, errors.New("Couldn't load cluster config, did you run devspace init")
213-
}
214-
215-
if (config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig) || config.Cluster.APIServer == nil {
216-
return clientcmd.BuildConfigFromFlags("", filepath.Join(fsutil.GetHomeDir(), ".kube", "config"))
217-
}
218-
return &rest.Config{
219-
Host: *config.Cluster.APIServer,
220-
Username: *config.Cluster.User.Username,
221-
TLSClientConfig: rest.TLSClientConfig{
222-
CAData: []byte(*config.Cluster.CaCert),
223-
CertData: []byte(*config.Cluster.User.ClientCert),
224-
KeyData: []byte(*config.Cluster.User.ClientKey),
225-
},
226-
}, nil
227-
}
228-
229238
// ForwardPorts forwards the specified ports from the cluster to the local machine
230239
func ForwardPorts(kubectlClient *kubernetes.Clientset, pod *k8sv1.Pod, ports []string, stopChan chan struct{}, readyChan chan struct{}) error {
231240
config, err := GetClientConfig()

pkg/devspace/config/v1/cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package v1
33
//Cluster is a struct that contains data for a Kubernetes-Cluster
44
type Cluster struct {
55
UseKubeConfig *bool `yaml:"useKubeConfig,omitempty"`
6+
KubeContext *string `yaml:"kubeContext,omitempty"`
67
APIServer *string `yaml:"apiServer,omitempty"`
78
CaCert *string `yaml:"caCert,omitempty"`
89
User *ClusterUser `yaml:"user,omitempty"`
910
}
1011

1112
//ClusterUser is a user with its username and its client certificate
1213
type ClusterUser struct {
13-
Username *string `yaml:"username,omitempty"`
1414
ClientCert *string `yaml:"clientCert,omitempty"`
1515
ClientKey *string `yaml:"clientKey,omitempty"`
1616
}

0 commit comments

Comments
 (0)