Skip to content

Commit 04e6e80

Browse files
authored
Merge pull request #268 from covexo/devspace-cloud
Devspace cloud
2 parents 7b43e28 + 8df7771 commit 04e6e80

8 files changed

Lines changed: 553 additions & 65 deletions

File tree

cmd/init.go

Lines changed: 108 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/cloud"
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,111 @@ 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) useCloudProvider() bool {
356+
providerConfig, err := cloud.ParseCloudConfig()
357+
if err != nil {
358+
log.Fatalf("Error loading cloud config: %v", err)
359+
}
360+
361+
if len(providerConfig) > 1 {
362+
cloudProvider := "("
363+
364+
for name := range providerConfig {
365+
if len(cloudProvider) > 1 {
366+
cloudProvider += ", "
367+
}
368+
369+
cloudProvider += name
370+
}
371+
372+
cloudProvider += ")"
373+
cloudProviderSelected := ""
374+
375+
for _, ok := providerConfig[cloudProviderSelected]; ok == false && cloudProviderSelected != "no"; {
376+
cloudProviderSelected = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
377+
Question: "Do you want to use a cloud provider? (no to skip) " + cloudProvider,
378+
DefaultValue: cloud.DevSpaceCloudProviderName,
379+
})
380+
}
381+
382+
if cloudProviderSelected != "no" {
383+
addToContext := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
384+
Question: "Do you want to add the cloud provider to the $HOME/.kube/config file? (yes | no)",
385+
DefaultValue: "yes",
386+
ValidationRegexPattern: "^(yes)|(no)$",
387+
}) == "yes"
388+
389+
cmd.config.Cluster.CloudProvider = &cloudProviderSelected
390+
cmd.config.Cluster.UseKubeConfig = &addToContext
391+
392+
err := cloud.Update(providerConfig, cmd.config, true)
393+
if err != nil {
394+
log.Fatalf("Couldn't authenticate to devspace cloud: %v", err)
395+
}
396+
397+
return true
398+
}
399+
} else {
400+
useDevSpaceCloud := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
401+
Question: "Do you want to use the devspace cloud? (free ready-to-use kubernetes) (yes | no)",
402+
DefaultValue: "yes",
403+
ValidationRegexPattern: "^(yes)|(no)$",
404+
}) == "yes"
405+
406+
if useDevSpaceCloud {
407+
addToContext := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
408+
Question: "Do you want to add the devspace-cloud to the $HOME/.kube/config file? (yes | no)",
409+
DefaultValue: "yes",
410+
ValidationRegexPattern: "^(yes)|(no)$",
411+
}) == "yes"
412+
413+
cmd.config.Cluster.CloudProvider = configutil.String(cloud.DevSpaceCloudProviderName)
414+
cmd.config.Cluster.UseKubeConfig = &addToContext
415+
416+
err := cloud.Update(providerConfig, cmd.config, true)
417+
if err != nil {
418+
log.Fatalf("Couldn't authenticate to devspace cloud: %v", err)
419+
}
420+
421+
return true
422+
}
423+
}
424+
425+
return false
351426
}
352427

353428
func (cmd *InitCmd) configureKubernetes() {
354429
clusterConfig := cmd.config.Cluster
355430
useKubeConfig := false
356-
homeDir, homeErr := homedir.Dir()
357431

358-
if homeErr != nil {
359-
log.With(homeErr).Fatalf("Unable to determine home dir")
432+
// Check if devspace cloud should be used
433+
if cmd.useCloudProvider() {
434+
return
360435
}
361-
kubeConfigPath := homeDir + "/.kube/config"
362436

363-
_, kubeConfigNotFound := os.Stat(kubeConfigPath)
364-
365-
if kubeConfigNotFound == nil {
437+
_, err := os.Stat(clientcmd.RecommendedHomeFile)
438+
if err == nil {
366439
skipAnswer := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
367440
Question: "Do you want to use your existing $HOME/.kube/config for Kubernetes access? (yes | no)",
368441
DefaultValue: "yes",
369442
ValidationRegexPattern: "^(yes)|(no)$",
370443
})
444+
371445
useKubeConfig = (*skipAnswer == "yes")
372446
}
373-
clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig)
374447

448+
clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig)
375449
if !useKubeConfig {
376450
if clusterConfig.APIServer == nil {
377451
clusterConfig.APIServer = configutil.String("https://192.168.99.100:8443")
@@ -393,15 +467,10 @@ func (cmd *InitCmd) configureKubernetes() {
393467

394468
if clusterConfig.User == nil {
395469
clusterConfig.User = &v1.ClusterUser{
396-
Username: configutil.String(""),
397470
ClientCert: configutil.String(""),
398471
ClientKey: configutil.String(""),
399472
}
400473
} else {
401-
if clusterConfig.User.Username == nil {
402-
clusterConfig.User.Username = configutil.String("")
403-
}
404-
405474
if clusterConfig.User.ClientCert == nil {
406475
clusterConfig.User.ClientCert = configutil.String("")
407476
}
@@ -410,11 +479,6 @@ func (cmd *InitCmd) configureKubernetes() {
410479
clusterConfig.User.ClientKey = configutil.String("")
411480
}
412481
}
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-
})
418482
clusterConfig.User.ClientCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{
419483
Question: "What is your Kubernetes client certificate? (PEM)",
420484
DefaultValue: *clusterConfig.User.ClientCert,
@@ -425,6 +489,13 @@ func (cmd *InitCmd) configureKubernetes() {
425489
DefaultValue: *clusterConfig.User.ClientKey,
426490
InputTerminationString: "-----END RSA PRIVATE KEY-----",
427491
})
492+
} else {
493+
currentContext, err := kubeconfig.GetCurrentContext()
494+
if err != nil {
495+
log.Fatalf("Couldn't determine current kubernetes context: %v", err)
496+
}
497+
498+
clusterConfig.KubeContext = &currentContext
428499
}
429500
}
430501

cmd/up.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,14 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) {
160160
mustRedeploy := cmd.buildImages()
161161

162162
// Check if we find a running release pod
163-
config := configutil.GetConfig(false)
164163
hash, err := hash.Directory("chart")
165164
if err != nil {
166165
log.Fatalf("Error hashing chart directory: %v", err)
167166
}
168167

168+
// Load config
169+
config := configutil.GetConfig(false)
170+
169171
pod, err := getRunningDevSpacePod(cmd.helm, cmd.kubectl)
170172
if err != nil || mustRedeploy || cmd.flags.deploy || config.DevSpace.ChartHash == nil || *config.DevSpace.ChartHash != hash {
171173
cmd.deployChart()

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: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import (
77
"io"
88
"net/http"
99
"os"
10-
"path/filepath"
1110
"sync"
1211

12+
"github.com/covexo/devspace/pkg/util/kubeconfig"
13+
14+
"github.com/covexo/devspace/pkg/devspace/cloud"
1315
"github.com/covexo/devspace/pkg/devspace/config/configutil"
14-
"github.com/covexo/devspace/pkg/util/fsutil"
1516
"github.com/covexo/devspace/pkg/util/log"
1617
dockerterm "github.com/docker/docker/pkg/term"
1718
k8sv1 "k8s.io/api/core/v1"
@@ -36,14 +37,57 @@ var isMinikubeVar *bool
3637
//NewClient creates a new kubernetes client
3738
func NewClient() (*kubernetes.Clientset, error) {
3839
config, err := GetClientConfig()
39-
4040
if err != nil {
4141
return nil, err
4242
}
4343

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+
// Update devspace cloud cluster config
55+
if config.Cluster.CloudProvider != nil && *config.Cluster.CloudProvider != "" {
56+
providerConfig, err := cloud.ParseCloudConfig()
57+
if err != nil {
58+
return nil, fmt.Errorf("Couldn't load cloud provider config: %v", err)
59+
}
60+
61+
err = cloud.Update(providerConfig, config, false)
62+
if err != nil {
63+
log.Warnf("Couldn't update cloud provider %s information: %v", *config.Cluster.CloudProvider, err)
64+
}
65+
}
66+
67+
if (config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig) || config.Cluster.APIServer == nil {
68+
// If we should use a certain kube context use that
69+
if config.Cluster.KubeContext != nil && len(*config.Cluster.KubeContext) > 0 {
70+
kubeConfig, err := kubeconfig.ReadKubeConfig(clientcmd.RecommendedHomeFile)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
return clientcmd.NewNonInteractiveClientConfig(*kubeConfig, *config.Cluster.KubeContext, nil, clientcmd.NewDefaultClientConfigLoadingRules()).ClientConfig()
76+
}
77+
78+
return clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
79+
}
80+
81+
return &rest.Config{
82+
Host: *config.Cluster.APIServer,
83+
TLSClientConfig: rest.TLSClientConfig{
84+
CAData: []byte(*config.Cluster.CaCert),
85+
CertData: []byte(*config.Cluster.User.ClientCert),
86+
KeyData: []byte(*config.Cluster.User.ClientKey),
87+
},
88+
}, nil
89+
}
90+
4791
// IsMinikube returns true if the Kubernetes cluster is a minikube
4892
func IsMinikube() bool {
4993
if isMinikubeVar == nil {
@@ -204,28 +248,6 @@ func GetPodsFromDeployment(kubectl *kubernetes.Clientset, deployment, namespace
204248
})
205249
}
206250

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-
229251
// ForwardPorts forwards the specified ports from the cluster to the local machine
230252
func ForwardPorts(kubectlClient *kubernetes.Clientset, pod *k8sv1.Pod, ports []string, stopChan chan struct{}, readyChan chan struct{}) error {
231253
config, err := GetClientConfig()

0 commit comments

Comments
 (0)