-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Compose] move dockerCompose and add images system
- Loading branch information
Showing
10 changed files
with
739 additions
and
375 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package compose | ||
|
||
import ( | ||
"testing" | ||
"fmt" | ||
) | ||
|
||
func TestInitServicesAttributes(t *testing.T) { | ||
attributes := InitServicesAttributes() | ||
if len(attributes) < 1 { | ||
t.Fatalf("Expected services attribute length to be > 0") | ||
} | ||
} | ||
|
||
func TestInitNetworksAttributes(t *testing.T) { | ||
attributes := InitNetworksAttributes() | ||
if len(attributes) < 1 { | ||
t.Fatalf("Expected networks attributes length to be > 0") | ||
} | ||
} | ||
|
||
func TestInitVolumesAttributes(t *testing.T) { | ||
attributes := InitVolumesAttributes() | ||
if len(attributes) < 1 { | ||
t.Fatalf("Expected volumes attributes length to be > 0") | ||
} | ||
} | ||
|
||
func TestDisplayHelpWhenDescriptionExists(t *testing.T) { | ||
description := "The image to start the container from. Can either be a repository/tag or a partial image ID" | ||
|
||
attribute := Attribute{ | ||
InputDescription: description, | ||
} | ||
|
||
helpMessage := attribute.DisplayHelp() | ||
expectedResult := fmt.Sprintf("%s", attribute.InputDescription) | ||
|
||
if helpMessage != expectedResult { | ||
t.Fatalf("Expected `%s` but got `%s`", expectedResult, helpMessage) | ||
} | ||
} | ||
|
||
func TestDisplayHelpWhenExampleExists(t *testing.T) { | ||
description := "The image to start the container from. Can either be a repository/tag or a partial image ID" | ||
|
||
attribute := Attribute{ | ||
InputDescription: description, | ||
Example: "nginx:alpine", | ||
} | ||
|
||
helpMessage := attribute.DisplayHelp() | ||
expectedResult := fmt.Sprintf("%s (Example: %s)", attribute.InputDescription, attribute.Example) | ||
|
||
if helpMessage != expectedResult { | ||
t.Fatalf("Expected `%s` but got `%s`", description, helpMessage) | ||
} | ||
} | ||
|
||
func TestDisplayHelpWhenDescriptionDoesNotExists(t *testing.T) { | ||
attribute := Attribute{} | ||
|
||
helpMessage := attribute.DisplayHelp() | ||
expectedResult := "" | ||
|
||
if helpMessage != expectedResult { | ||
t.Fatalf("Expected `` but got `%s`", helpMessage) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Manage docker-compose configuration file. | ||
|
||
package compose | ||
|
||
import ( | ||
"io/ioutil" | ||
"gopkg.in/yaml.v2" | ||
"os" | ||
"path/filepath" | ||
"fmt" | ||
"strings" | ||
"regexp" | ||
"github.com/kariae/composei/reader" | ||
"github.com/kariae/composei/logger" | ||
) | ||
|
||
// A DockerCompose represents a docker-compose configuration file | ||
type DockerCompose struct { | ||
Filename string | ||
EnvFilename string | ||
Version string | ||
EnvVarsRegex *regexp.Regexp | ||
Values yaml.MapSlice | ||
} | ||
|
||
// New instantiates a new docker-compose configuration file. | ||
func New() *DockerCompose { | ||
self := &DockerCompose{} | ||
|
||
self.Filename = "docker-compose.yml" | ||
self.EnvFilename = ".env" | ||
self.Version = "3" | ||
self.EnvVarsRegex = regexp.MustCompile(`\${([^}]+)}`) | ||
self.Values = yaml.MapSlice{} | ||
|
||
return self | ||
} | ||
|
||
// FileExists check if the docker-compose configuration file exists | ||
// or not in the working directory. | ||
func (dockerCompose *DockerCompose) FileExists() bool { | ||
wd := getWorkingDir() | ||
if _, err := os.Stat(filepath.FromSlash(fmt.Sprintf("%s/%s", wd, dockerCompose.Filename))); os.IsNotExist(err) { | ||
return false | ||
} else { | ||
return true | ||
} | ||
} | ||
|
||
// LoadFile loads docker-compose file and unmarshal its content in | ||
// DockerCompose Values attribute. | ||
func (dockerCompose *DockerCompose) LoadFile() (error) { | ||
var err error | ||
|
||
source, err := ioutil.ReadFile(dockerCompose.Filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = yaml.Unmarshal([]byte(source), &dockerCompose.Values) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// AddService Add a `service` to DockerCompose | ||
func (dockerCompose *DockerCompose) AddService(r reader.InputReader, service yaml.MapItem) { | ||
dockerCompose.addItemToTopLevel(r, "services", service) | ||
} | ||
|
||
// AddService Add a `network` to DockerCompose | ||
func (dockerCompose *DockerCompose) AddNetwork(r reader.InputReader, network yaml.MapItem) { | ||
dockerCompose.addItemToTopLevel(r, "networks", network) | ||
} | ||
|
||
// AddService Add a `volume` to DockerCompose | ||
func (dockerCompose *DockerCompose) AddVolume(r reader.InputReader, volume yaml.MapItem) { | ||
dockerCompose.addItemToTopLevel(r, "volumes", volume) | ||
} | ||
|
||
// AddService Add an element to a top level DockerCompose value | ||
func (dockerCompose *DockerCompose) addItemToTopLevel(r reader.InputReader, topLevelName string, item yaml.MapItem) { | ||
topLevelIndex := dockerCompose.CreateTopLevel(topLevelName, yaml.MapSlice{}) | ||
|
||
// Check if item already exists | ||
addItem := true | ||
topLevelItems := dockerCompose.Values[topLevelIndex].Value.(yaml.MapSlice) | ||
if topLevelName == "services" { | ||
for i, existingItem := range topLevelItems { | ||
if existingItem.Key == item.Key { | ||
// Plural -> singular with the trivial way possible xD | ||
switch reader.ReadLine(r, fmt.Sprintf("A `%s` already exists with the same name, would you like to replace it", strings.TrimRight(topLevelName, "s")), []string{reader.YesChoice, reader.NoChoice}, false, "") { | ||
case reader.YesChoice: | ||
topLevelItems = append(topLevelItems[:i], topLevelItems[i+1:]...) | ||
case reader.NoChoice: | ||
addItem = false | ||
} | ||
} | ||
} | ||
} | ||
|
||
if addItem { | ||
dockerCompose.Values[topLevelIndex].Value = append(topLevelItems, yaml.MapSlice{item}...) | ||
} | ||
} | ||
|
||
// CreateTopLevel Create a top level (services, networks or volumes) value | ||
func (dockerCompose *DockerCompose) CreateTopLevel(topLevelName string, value interface{}) (index int) { | ||
exists, index, content := dockerCompose.topLevelExists(topLevelName) | ||
if !exists { | ||
content = yaml.MapItem{Key:topLevelName, Value:value} | ||
dockerCompose.Values = append(dockerCompose.Values, content) | ||
index = len(dockerCompose.Values) - 1 | ||
} | ||
|
||
return | ||
} | ||
|
||
// topLevelExists checks if a top level value (services, networks or volumes) exists | ||
func (dockerCompose *DockerCompose) topLevelExists(key string) (exists bool, index int, content yaml.MapItem) { | ||
exists = false | ||
index = 0 | ||
for i,v := range dockerCompose.Values { | ||
if v.Key == key { | ||
exists = true | ||
index = i | ||
content = dockerCompose.Values[i] | ||
break | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
// Save saves dockerCompose in a YAML configuration file | ||
// If environment variables are set they're saved to the | ||
// appropriate .env file. | ||
func (dockerCompose *DockerCompose) Save(r reader.InputReader, noEnv bool) error { | ||
var err error | ||
|
||
d, err := yaml.Marshal(&dockerCompose.Values) | ||
|
||
if err == nil { | ||
err = ioutil.WriteFile(dockerCompose.Filename, d, 0644) | ||
} | ||
|
||
// Generate env file | ||
if !noEnv { | ||
err = dockerCompose.generateEnvFile(r, string(d)) | ||
} | ||
|
||
return err | ||
} | ||
|
||
// generateEnvFile save environment variables in .env file | ||
func (dockerCompose *DockerCompose) generateEnvFile(r reader.InputReader, composeFileContent string) error { | ||
var err error | ||
envVars := make(map[string]bool) | ||
envFileContent := "# env file generated by Composei\n" | ||
|
||
if matches := dockerCompose.EnvVarsRegex.FindAllStringSubmatch(composeFileContent, -1); matches != nil { | ||
fmt.Println("Please enter the environment variables value") | ||
|
||
for _, envVarMatch := range matches { | ||
if _, ok := envVars[envVarMatch[1]]; !ok { | ||
envVars[envVarMatch[1]] = true | ||
|
||
envVarValue := reader.ReadLine(r, fmt.Sprintf("%s", envVarMatch[1]), []string{}, false, "") | ||
envFileContent += fmt.Sprintf("%s=%s\n", envVarMatch[1], envVarValue) | ||
} | ||
} | ||
|
||
err = ioutil.WriteFile(dockerCompose.EnvFilename, []byte(envFileContent), 0644) | ||
} | ||
|
||
return err | ||
} | ||
|
||
// getWorkingDir Get the current working directory | ||
func getWorkingDir() string { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
logger.ERROR(err.Error()) | ||
} | ||
|
||
return wd | ||
} | ||
|
||
|
Oops, something went wrong.