Skip to content

Commit 3cb6559

Browse files
authored
Merge pull request #1950 from lizardruss/master
Init: Docker Compose v6 Refactor
2 parents eaa2d35 + 4e43653 commit 3cb6559

180 files changed

Lines changed: 20324 additions & 2470 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/init.go

Lines changed: 190 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strconv"
1212
"strings"
1313

14+
"github.com/loft-sh/devspace/pkg/devspace/compose"
1415
"github.com/loft-sh/devspace/pkg/devspace/config/localcache"
1516
"github.com/sirupsen/logrus"
1617

@@ -60,6 +61,8 @@ const (
6061

6162
// InitCmd is a struct that defines a command call for "init"
6263
type InitCmd struct {
64+
*flags.GlobalFlags
65+
6366
// Flags
6467
Reconfigure bool
6568
Dockerfile string
@@ -71,7 +74,8 @@ type InitCmd struct {
7174
// NewInitCmd creates a new init command
7275
func NewInitCmd(f factory.Factory) *cobra.Command {
7376
cmd := &InitCmd{
74-
log: f.GetLog(),
77+
log: f.GetLog(),
78+
GlobalFlags: globalFlags,
7579
}
7680

7781
initCmd := &cobra.Command{
@@ -147,45 +151,31 @@ func (cmd *InitCmd) Run(f factory.Factory) error {
147151
// Print DevSpace logo
148152
log.PrintLogo()
149153

150-
/*
151-
generateFromDockerCompose := false
152-
// TODO: Enable again
153-
dockerComposePath := "" // compose.GetDockerComposePath()
154-
if dockerComposePath != "" {
155-
selectedDockerComposeOption, err := cmd.log.Question(&survey.QuestionOptions{
156-
Question: "Docker Compose configuration detected. Do you want to create a DevSpace configuration based on Docker Compose?",
157-
DefaultValue: DockerComposeDevSpaceConfigOption,
158-
Options: []string{
159-
DockerComposeDevSpaceConfigOption,
160-
NewDevSpaceConfigOption,
161-
},
162-
})
163-
if err != nil {
164-
return err
165-
}
154+
// Determine if we're initializing from scratch, or using docker-compose.yaml
155+
dockerComposePath, generateFromDockerCompose, err := cmd.shouldGenerateFromDockerCompose()
156+
if err != nil {
157+
return err
158+
}
166159

167-
generateFromDockerCompose = selectedDockerComposeOption == DockerComposeDevSpaceConfigOption
168-
}
160+
if generateFromDockerCompose {
161+
err = cmd.initDockerCompose(f, dockerComposePath)
162+
} else {
163+
err = cmd.initDevspace(f, configLoader)
164+
}
169165

170-
if generateFromDockerCompose {
171-
composeLoader := compose.NewDockerComposeLoader(dockerComposePath)
172-
if err != nil {
173-
return err
174-
}
166+
if err != nil {
167+
panic(err)
168+
}
175169

176-
// Load config
177-
config, err := composeLoader.Load(cmd.log)
178-
if err != nil {
179-
return err
180-
}
170+
cmd.log.WriteString(logrus.InfoLevel, "\n")
171+
cmd.log.Done("Project successfully initialized")
172+
cmd.log.Info("Configuration saved in devspace.yaml - you can make adjustments as needed")
173+
cmd.log.Infof("\r \nYou can now run:\n1. %s - to pick which Kubernetes namespace to work in\n2. %s - to start developing your project in Kubernetes\n\nRun `%s` or `%s` to see a list of available commands and flags\n", ansi.Color("devspace use namespace", "blue+b"), ansi.Color("devspace dev", "blue+b"), ansi.Color("devspace -h", "blue+b"), ansi.Color("devspace [command] -h", "blue+b"))
181174

182-
// Save config
183-
err = composeLoader.Save(config)
184-
if err != nil {
185-
return err
186-
}
187-
} else {*/
175+
return nil
176+
}
188177

178+
func (cmd *InitCmd) initDevspace(f factory.Factory, configLoader loader.ConfigLoader) error {
189179
// Create new dockerfile generator
190180
languageHandler, err := generator.NewLanguageHandler("", "", cmd.log)
191181
if err != nil {
@@ -214,7 +204,7 @@ func (cmd *InitCmd) Run(f factory.Factory) error {
214204
var config *latest.Config
215205

216206
// create kubectl client
217-
client, err := f.NewKubeClientFromContext(globalFlags.KubeContext, globalFlags.Namespace)
207+
client, err := f.NewKubeClientFromContext(cmd.GlobalFlags.KubeContext, cmd.GlobalFlags.Namespace)
218208
if err == nil {
219209
configInterface, err := configLoader.Load(context.TODO(), client, &loader.ConfigOptions{}, cmd.log)
220210
if err == nil {
@@ -476,8 +466,6 @@ create_deployments --all \ # 3. Deploy Helm charts and ma
476466
return err
477467
}
478468

479-
/*}*/
480-
481469
// Save generated
482470
err = localCache.Save()
483471
if err != nil {
@@ -491,6 +479,88 @@ create_deployments --all \ # 3. Deploy Helm charts and ma
491479
}
492480

493481
configPath := loader.ConfigPath("")
482+
err = annotateConfig(configPath)
483+
if err != nil {
484+
return err
485+
}
486+
487+
return nil
488+
}
489+
490+
func (cmd *InitCmd) initDockerCompose(f factory.Factory, composePath string) error {
491+
project, err := compose.LoadDockerComposeProject(composePath)
492+
if err != nil {
493+
return err
494+
}
495+
496+
projectName, _, err := getProjectName()
497+
if err != nil {
498+
return err
499+
}
500+
501+
project.Name = projectName
502+
503+
// Prompt user for entrypoints for each container with sync folders.
504+
for idx, service := range project.Services {
505+
localPaths := compose.GetServiceSyncPaths(project, service)
506+
noEntryPoint := len(service.Entrypoint) == 0
507+
hasSyncEndpoints := len(localPaths) > 0
508+
509+
if noEntryPoint && hasSyncEndpoints {
510+
entrypointStr, err := cmd.log.Question(&survey.QuestionOptions{
511+
Question: "How is this container started? (e.g. npm start, gradle run, go run main.go)",
512+
})
513+
if err != nil {
514+
return err
515+
}
516+
517+
entrypoint := strings.Split(entrypointStr, " ")
518+
project.Services[idx].Entrypoint = entrypoint
519+
}
520+
}
521+
522+
// Generate DevSpace configuration
523+
composeManager := compose.NewComposeManager(project)
524+
err = composeManager.Load(cmd.log)
525+
if err != nil {
526+
return err
527+
}
528+
529+
// Save each configuration file
530+
for path, config := range composeManager.Configs() {
531+
localCache, err := localcache.NewCacheLoader().Load(path)
532+
if err != nil {
533+
return err
534+
}
535+
536+
// Save config
537+
err = loader.Save(path, config)
538+
if err != nil {
539+
return err
540+
}
541+
542+
// Save generated
543+
err = localCache.Save()
544+
if err != nil {
545+
return errors.Errorf("Error saving generated file: %v", err)
546+
}
547+
548+
// Add .devspace/ to .gitignore
549+
err = appendToIgnoreFile(gitIgnoreFile, devspaceFolderGitignore)
550+
if err != nil {
551+
cmd.log.Warn(err)
552+
}
553+
554+
err = annotateConfig(path)
555+
if err != nil {
556+
return err
557+
}
558+
}
559+
560+
return nil
561+
}
562+
563+
func annotateConfig(configPath string) error {
494564
annotatedConfig, err := ioutil.ReadFile(configPath)
495565
if err != nil {
496566
panic(err)
@@ -541,74 +611,9 @@ create_deployments --all \ # 3. Deploy Helm charts and ma
541611
return err
542612
}
543613

544-
cmd.log.WriteString(logrus.InfoLevel, "\n")
545-
cmd.log.Done("Project successfully initialized")
546-
cmd.log.Info("Configuration saved in devspace.yaml - you can make adjustments as needed")
547-
cmd.log.Infof("\r \nYou can now run:\n1. %s - to pick which Kubernetes namespace to work in\n2. %s - to start developing your project in Kubernetes\n\nRun `%s` or `%s` to see a list of available commands and flags\n", ansi.Color("devspace use namespace", "blue+b"), ansi.Color("devspace dev", "blue+b"), ansi.Color("devspace -h", "blue+b"), ansi.Color("devspace [command] -h", "blue+b"))
548-
return nil
549-
}
550-
551-
func appendToIgnoreFile(ignoreFile, content string) error {
552-
// Check if ignoreFile exists
553-
_, err := os.Stat(ignoreFile)
554-
if os.IsNotExist(err) {
555-
_ = fsutil.WriteToFile([]byte(content), ignoreFile)
556-
} else {
557-
fileContent, err := ioutil.ReadFile(ignoreFile)
558-
if err != nil {
559-
return errors.Errorf("Error reading file %s: %v", ignoreFile, err)
560-
}
561-
562-
// append only if not found in file content
563-
if !strings.Contains(string(fileContent), content) {
564-
file, err := os.OpenFile(ignoreFile, os.O_APPEND|os.O_WRONLY, 0600)
565-
if err != nil {
566-
return errors.Errorf("Error writing file %s: %v", ignoreFile, err)
567-
}
568-
569-
defer file.Close()
570-
if _, err = file.WriteString(content); err != nil {
571-
return errors.Errorf("Error writing file %s: %v", ignoreFile, err)
572-
}
573-
}
574-
}
575614
return nil
576615
}
577616

578-
func getProjectName() (string, string, error) {
579-
projectName := ""
580-
projectNamespace := ""
581-
gitRemote, err := command.Output(context.TODO(), "", "git", "config", "--get", "remote.origin.url")
582-
if err == nil {
583-
sep := "/"
584-
projectParts := strings.Split(string(regexp.MustCompile(`^.*?://[^/]+?/([^.]+)(\.git)?`).ReplaceAll(gitRemote, []byte("$1"))), sep)
585-
partsLen := len(projectParts)
586-
if partsLen > 1 {
587-
projectNamespace = strings.Join(projectParts[0:partsLen-1], sep)
588-
projectName = projectParts[partsLen-1]
589-
}
590-
}
591-
592-
if projectName == "" {
593-
absPath, err := filepath.Abs(".")
594-
if err != nil {
595-
return "", "", err
596-
}
597-
projectName = filepath.Base(absPath)
598-
}
599-
600-
projectName = strings.ToLower(projectName)
601-
projectName = regexp.MustCompile("[^a-zA-Z0-9- ]+").ReplaceAllString(projectName, "")
602-
projectName = regexp.MustCompile("[^a-zA-Z0-9-]+").ReplaceAllString(projectName, "-")
603-
projectName = strings.Trim(projectName, "-")
604-
605-
if !SpaceNameValidationRegEx.MatchString(projectName) || len(projectName) > 42 {
606-
projectName = "devspace"
607-
}
608-
609-
return projectName, projectNamespace, nil
610-
}
611-
612617
func (cmd *InitCmd) addDevConfig(config *latest.Config, imageName, image string, port int, languageHandler *generator.LanguageHandler) error {
613618
if config.Dev == nil {
614619
config.Dev = map[string]*latest.DevPod{}
@@ -726,6 +731,7 @@ func (cmd *InitCmd) render(f factory.Factory, config *latest.Config) (string, er
726731
Silent: true,
727732
ConfigPath: renderPath,
728733
},
734+
Pipeline: "deploy",
729735
SkipPush: true,
730736
SkipBuild: true,
731737
Render: true,
@@ -739,6 +745,87 @@ func (cmd *InitCmd) render(f factory.Factory, config *latest.Config) (string, er
739745
return writer.String(), nil
740746
}
741747

748+
func (cmd *InitCmd) shouldGenerateFromDockerCompose() (string, bool, error) {
749+
dockerComposePath := compose.GetDockerComposePath()
750+
if dockerComposePath != "" {
751+
selectedDockerComposeOption, err := cmd.log.Question(&survey.QuestionOptions{
752+
Question: "Docker Compose configuration detected. Do you want to create a DevSpace configuration based on Docker Compose?",
753+
DefaultValue: DockerComposeDevSpaceConfigOption,
754+
Options: []string{
755+
DockerComposeDevSpaceConfigOption,
756+
NewDevSpaceConfigOption,
757+
},
758+
})
759+
if err != nil {
760+
return "", false, err
761+
}
762+
763+
return dockerComposePath, selectedDockerComposeOption == DockerComposeDevSpaceConfigOption, nil
764+
}
765+
return "", false, nil
766+
}
767+
768+
func appendToIgnoreFile(ignoreFile, content string) error {
769+
// Check if ignoreFile exists
770+
_, err := os.Stat(ignoreFile)
771+
if os.IsNotExist(err) {
772+
_ = fsutil.WriteToFile([]byte(content), ignoreFile)
773+
} else {
774+
fileContent, err := ioutil.ReadFile(ignoreFile)
775+
if err != nil {
776+
return errors.Errorf("Error reading file %s: %v", ignoreFile, err)
777+
}
778+
779+
// append only if not found in file content
780+
if !strings.Contains(string(fileContent), content) {
781+
file, err := os.OpenFile(ignoreFile, os.O_APPEND|os.O_WRONLY, 0600)
782+
if err != nil {
783+
return errors.Errorf("Error writing file %s: %v", ignoreFile, err)
784+
}
785+
786+
defer file.Close()
787+
if _, err = file.WriteString(content); err != nil {
788+
return errors.Errorf("Error writing file %s: %v", ignoreFile, err)
789+
}
790+
}
791+
}
792+
return nil
793+
}
794+
795+
func getProjectName() (string, string, error) {
796+
projectName := ""
797+
projectNamespace := ""
798+
gitRemote, err := command.Output(context.TODO(), "", "git", "config", "--get", "remote.origin.url")
799+
if err == nil {
800+
sep := "/"
801+
projectParts := strings.Split(string(regexp.MustCompile(`^.*?://[^/]+?/([^.]+)(\.git)?`).ReplaceAll(gitRemote, []byte("$1"))), sep)
802+
partsLen := len(projectParts)
803+
if partsLen > 1 {
804+
projectNamespace = strings.Join(projectParts[0:partsLen-1], sep)
805+
projectName = projectParts[partsLen-1]
806+
}
807+
}
808+
809+
if projectName == "" {
810+
absPath, err := filepath.Abs(".")
811+
if err != nil {
812+
return "", "", err
813+
}
814+
projectName = filepath.Base(absPath)
815+
}
816+
817+
projectName = strings.ToLower(projectName)
818+
projectName = regexp.MustCompile("[^a-zA-Z0-9- ]+").ReplaceAllString(projectName, "")
819+
projectName = regexp.MustCompile("[^a-zA-Z0-9-]+").ReplaceAllString(projectName, "-")
820+
projectName = strings.Trim(projectName, "-")
821+
822+
if !SpaceNameValidationRegEx.MatchString(projectName) || len(projectName) > 42 {
823+
projectName = "devspace"
824+
}
825+
826+
return projectName, projectNamespace, nil
827+
}
828+
742829
func parseImages(manifests string) ([]string, error) {
743830
images := []string{}
744831

0 commit comments

Comments
 (0)