Skip to content

Commit

Permalink
Add generator pass to add Watchtower (#247)
Browse files Browse the repository at this point in the history
* Add new pass to add Watchtower to the stack

* Fix tests

* Add test for GetAllLabels

* Extend

* Fix bug

* Extend

* Fix bug

* Fix test

* Optimize service tester script

* Add more tests

* Fix React template
  • Loading branch information
marcauberer committed Dec 15, 2021
1 parent 0a294f6 commit d252645
Show file tree
Hide file tree
Showing 19 changed files with 473 additions and 11 deletions.
5 changes: 4 additions & 1 deletion .github/scripts/service-tester/predefined-service-tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ def test_combination(comb):
remove(BIN_PATH + "/config.yml")

# Execute Compose Generator with the config file
if system(f"cd {BIN_PATH} && docker compose build --quiet") != 0:
sys.exit("Docker failed when building the local service(s) for combination " + str(comb))

if system(f"cd {BIN_PATH} && docker compose up -d --quiet-pull") != 0:
sys.exit("Docker failed when generating stack for combination " + str(comb))
sys.exit("Docker failed when pulling the remote service(s) for combination " + str(comb))

if system(f"cd {BIN_PATH} && docker compose down") != 0:
sys.exit("Error on 'docker compose down' for " + str(comb))
Expand Down
3 changes: 3 additions & 0 deletions .github/scripts/service-validator/config-schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
'proxied': {
'type': 'boolean'
},
'auto-updated': {
'type': 'boolean'
},
'demoAppInitCmd': {
'type': 'list'
},
Expand Down
1 change: 1 addition & 0 deletions predefined-services/db-admin/adminer/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"label": "Adminer",
"preselected": "services.database contains name == \"postgres\"",
"proxied": true,
"auto-updated": true,
"files": [
{
"path": "service.yml",
Expand Down
1 change: 1 addition & 0 deletions predefined-services/db-admin/elasticsearch-hq/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"label": "ElasticsearchHQ",
"preselected": "services.database contains name == \"elasticsearch\"",
"proxied": true,
"auto-updated": true,
"files": [
{
"path": "service.yml",
Expand Down
1 change: 1 addition & 0 deletions predefined-services/db-admin/pgadmin/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"label": "pgAdmin",
"preselected": "false",
"proxied": true,
"auto-updated": true,
"files": [
{
"path": "service.yml",
Expand Down
1 change: 1 addition & 0 deletions predefined-services/db-admin/phpmyadmin/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"label": "PhpMyAdmin",
"preselected": "services.database contains name == \"mysql\" | services.database contains name == \"mariadb\"",
"proxied": true,
"auto-updated": true,
"files": [
{
"path": "service.yml",
Expand Down
1 change: 1 addition & 0 deletions predefined-services/db-admin/redis-insight/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"label": "RedisInsight",
"preselected": "services.database contains name == \"redis\"",
"proxied": true,
"auto-updated": true,
"files": [
{
"path": "service.yml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
FROM node:lts AS dev

WORKDIR /code
COPY yarn.lock /code/yarn.lock
COPY package.json /code/package.json
RUN yarn install --production

Expand Down
1 change: 1 addition & 0 deletions src/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func EnrichProjectWithServices(project *model.CGProject, config *model.GenerateC
generateChooseBackendsPass(project, availableTemplates, selectedTemplates, config)
generateChooseDatabasesPass(project, availableTemplates, selectedTemplates, config)
generateChooseDbAdminsPass(project, availableTemplates, selectedTemplates, config)
generateAddWatchtowerPass(project, selectedTemplates, config)

// Execute passes
generatePass(project, selectedTemplates)
Expand Down
10 changes: 10 additions & 0 deletions src/cmd/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func TestEnrichProjectWithServices1(t *testing.T) {
generateChooseTlsHelpersPass = func(project *model.CGProject, available *model.AvailableTemplates, selected *model.SelectedTemplates, config *model.GenerateConfig) {
generateChooseTlsHelpersPassCallCount++
}
generateAddWatchtowerPassCallCount := 0
generateAddWatchtowerPass = func(project *model.CGProject, selectedTemplates *model.SelectedTemplates, config *model.GenerateConfig) {
generateAddWatchtowerPassCallCount++
}
generatePassCallCount := 0
generatePass = func(project *model.CGProject, selectedTemplates *model.SelectedTemplates) {
generatePassCallCount++
Expand Down Expand Up @@ -130,6 +134,7 @@ func TestEnrichProjectWithServices1(t *testing.T) {
assert.Equal(t, 1, generateChooseDbAdminsPassCallCount)
assert.Equal(t, 1, generateChooseProxiesPassCallCount)
assert.Equal(t, 1, generateChooseTlsHelpersPassCallCount)
assert.Equal(t, 1, generateAddWatchtowerPassCallCount)
assert.Equal(t, 1, generatePassCallCount)
assert.Equal(t, 1, generateResolveDependencyGroupsPassCallCount)
assert.Equal(t, 1, generateSecretsPassCallCount)
Expand Down Expand Up @@ -208,6 +213,10 @@ func TestEnrichProjectWithServices2(t *testing.T) {
generateChooseTlsHelpersPass = func(project *model.CGProject, available *model.AvailableTemplates, selected *model.SelectedTemplates, config *model.GenerateConfig) {
generateChooseTlsHelpersPassCallCount++
}
generateAddWatchtowerPassCallCount := 0
generateAddWatchtowerPass = func(project *model.CGProject, selectedTemplates *model.SelectedTemplates, config *model.GenerateConfig) {
generateAddWatchtowerPassCallCount++
}
generatePassCallCount := 0
generatePass = func(project *model.CGProject, selectedTemplates *model.SelectedTemplates) {
generatePassCallCount++
Expand Down Expand Up @@ -254,6 +263,7 @@ func TestEnrichProjectWithServices2(t *testing.T) {
assert.Equal(t, 1, generateChooseDbAdminsPassCallCount)
assert.Equal(t, 1, generateChooseProxiesPassCallCount)
assert.Equal(t, 1, generateChooseTlsHelpersPassCallCount)
assert.Equal(t, 1, generateAddWatchtowerPassCallCount)
assert.Equal(t, 1, generatePassCallCount)
assert.Equal(t, 1, generateResolveDependencyGroupsPassCallCount)
assert.Equal(t, 1, generateSecretsPassCallCount)
Expand Down
1 change: 1 addition & 0 deletions src/cmd/mock_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ var generateChooseFrontendsPass = genPass.GenerateChooseFrontends
var generateChooseBackendsPass = genPass.GenerateChooseBackends
var generateChooseDatabasesPass = genPass.GenerateChooseDatabases
var generateChooseDbAdminsPass = genPass.GenerateChooseDbAdmins
var generateAddWatchtowerPass = genPass.GenerateAddWatchtower
var generatePass = genPass.Generate
var generateResolveDependencyGroupsPass = genPass.GenerateResolveDependencyGroups
var generateSecretsPass = genPass.GenerateSecrets
Expand Down
34 changes: 34 additions & 0 deletions src/model/predefined-template.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,39 @@ func (t SelectedTemplates) GetAll() []PredefinedTemplateConfig {
return templates
}

// GetAllRef returns the references to all templates of all types, mixed in a single slice
func (t SelectedTemplates) GetAllRef() []*PredefinedTemplateConfig {
var templates []*PredefinedTemplateConfig
for i := 0; i < len(t.FrontendServices); i++ {
templates = append(templates, &t.FrontendServices[i])
}
for i := 0; i < len(t.BackendServices); i++ {
templates = append(templates, &t.BackendServices[i])
}
for i := 0; i < len(t.DatabaseServices); i++ {
templates = append(templates, &t.DatabaseServices[i])
}
for i := 0; i < len(t.DbAdminServices); i++ {
templates = append(templates, &t.DbAdminServices[i])
}
for i := 0; i < len(t.ProxyServices); i++ {
templates = append(templates, &t.ProxyServices[i])
}
for i := 0; i < len(t.TlsHelperServices); i++ {
templates = append(templates, &t.TlsHelperServices[i])
}
return templates
}

// GetAllLabels returns the labels of all selected services
func (t SelectedTemplates) GetAllLabels() []string {
result := []string{}
for _, template := range t.GetAll() {
result = append(result, template.Label)
}
return result
}

// GetTotal returns the total number of selected templates
func (t SelectedTemplates) GetTotal() int {
count := len(t.FrontendServices)
Expand Down Expand Up @@ -105,6 +138,7 @@ type PredefinedTemplateConfig struct {
Type string `json:"-"`
Preselected string `json:"preselected,omitempty"`
Proxied bool `json:"proxied,omitempty"`
AutoUpdated bool `json:"auto-updated,omitempty"`
DemoAppInitCmd []string `json:"demoAppInitCmd,omitempty"`
ServiceInitCmd []string `json:"serviceInitCmd,omitempty"`
Files []File `json:"files,omitempty"`
Expand Down
121 changes: 121 additions & 0 deletions src/model/predefined-template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,127 @@ func TestGetAll(t *testing.T) {
assert.EqualValues(t, expectedResult, result)
}

// --------------------------------------------------------------------- GetAllRef -----------------------------------------------------------------

func TestGetAllRef(t *testing.T) {
// Test data
selectedTemplates := SelectedTemplates{
FrontendServices: []PredefinedTemplateConfig{
{Name: "angular"},
{Name: "vue"},
},
BackendServices: []PredefinedTemplateConfig{
{Name: "django"},
{Name: "flask"},
{Name: "rocket"},
},
DatabaseServices: []PredefinedTemplateConfig{
{Name: "faunadb"},
{Name: "mongodb"},
},
DbAdminServices: []PredefinedTemplateConfig{
{Name: "pgadmin"},
},
ProxyServices: []PredefinedTemplateConfig{
{Name: "nginx"},
},
TlsHelperServices: []PredefinedTemplateConfig{
{Name: "letsencrypt"},
},
}
expectedSelectedTemplates := SelectedTemplates{
FrontendServices: []PredefinedTemplateConfig{
{Name: "Changed value"},
{Name: "vue"},
},
BackendServices: []PredefinedTemplateConfig{
{Name: "django"},
{Name: "flask"},
{Name: "Changed value 2"},
},
DatabaseServices: []PredefinedTemplateConfig{
{Name: "faunadb"},
{Name: "mongodb"},
},
DbAdminServices: []PredefinedTemplateConfig{
{Name: "pgadmin"},
},
ProxyServices: []PredefinedTemplateConfig{
{Name: "nginx"},
},
TlsHelperServices: []PredefinedTemplateConfig{
{Name: "letsencrypt"},
},
}
// Execute test
result := selectedTemplates.GetAllRef()
result[0].Name = "Changed value"
result[4].Name = "Changed value 2"
// Assert
assert.Equal(t, expectedSelectedTemplates, selectedTemplates)
}

// -------------------------------------------------------------------- GetAllLabels ---------------------------------------------------------------

func TestGetAllLabels(t *testing.T) {
// Test data
selectedTemplates := SelectedTemplates{
FrontendServices: []PredefinedTemplateConfig{
{
Name: "drupal",
Label: "Drupal",
},
{
Name: "mediawiki",
Label: "Mediawiki",
},
},
BackendServices: []PredefinedTemplateConfig{
{
Name: "fiber",
Label: "Fiber",
},
{
Name: "nexus",
Label: "Nexus",
},
},
DatabaseServices: []PredefinedTemplateConfig{
{
Name: "scylladb",
Label: "ScyllaDB",
},
{
Name: "singlestore",
Label: "SingleStore",
},
},
DbAdminServices: []PredefinedTemplateConfig{
{
Name: "elasticsearch-hq",
Label: "Elasticsearch HQ",
},
{
Name: "redis-insight",
Label: "Redis Insight",
},
},
ProxyServices: []PredefinedTemplateConfig{
{
Name: "traefik",
Label: "Traefik",
},
},
TlsHelperServices: []PredefinedTemplateConfig{},
}
expectedResult := []string{"Drupal", "Mediawiki", "Fiber", "Nexus", "ScyllaDB", "SingleStore", "Elasticsearch HQ", "Redis Insight", "Traefik"}
// Execute test
result := selectedTemplates.GetAllLabels()
// Assert
assert.Equal(t, 9, len(result))
assert.EqualValues(t, expectedResult, result)
}

// ---------------------------------------------------------------------- GetTotal -----------------------------------------------------------------

func TestTotal(t *testing.T) {
Expand Down
69 changes: 69 additions & 0 deletions src/pass/generate/gen_add_watchtower.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package pass

import (
"compose-generator/model"

"github.com/compose-spec/compose-go/types"
)

func GenerateAddWatchtower(
project *model.CGProject,
selectedTemplates *model.SelectedTemplates,
config *model.GenerateConfig,
) {
// Ask the user if watchtower should be added to the project
pel()
templates := selectedTemplates.GetAllRef()
addWatchtower := false
if config == nil || !config.FromFile {
addWatchtower = yesNoQuestion("Do you want to add Watchtower to check for new image versions?", false)
}
if addWatchtower {
infoLogger.Println("Adding Watchtower to the project ...")
// Ask which services should be equipped with image update detection
allLabels := []string{}
preselectedLabels := []string{}
for _, template := range templates {
allLabels = append(allLabels, template.Label)
if template.AutoUpdated {
preselectedLabels = append(preselectedLabels, template.Label)
template.AutoUpdated = false
}
}
// Ask the user which of the services should be auto-updated
selectedIndices := multiSelectMenuQuestionIndex("For which services do you want to add Watchtower?", allLabels, preselectedLabels)
for _, i := range selectedIndices {
template := templates[i]
template.AutoUpdated = true
}

// Add watchtower service
project.Composition.Services = append(project.Composition.Services, types.ServiceConfig{
Name: "companion-watchtower",
Image: "containrrr/watchtower:latest",
Restart: types.RestartPolicyUnlessStopped,
Volumes: []types.ServiceVolumeConfig{
{
Type: types.VolumeTypeBind,
Source: "/var/run/docker.sock",
Target: "/var/run/docker.sock",
},
},
DependsOn: types.DependsOnConfig{
model.TemplateTypeFrontend: types.ServiceDependency{},
model.TemplateTypeBackend: types.ServiceDependency{},
model.TemplateTypeDatabase: types.ServiceDependency{},
model.TemplateTypeDbAdmin: types.ServiceDependency{},
model.TemplateTypeProxy: types.ServiceDependency{},
model.TemplateTypeTlsHelper: types.ServiceDependency{},
},
Command: types.ShellCommand{"--interval", "30"},
})
infoLogger.Println("Adding Watchtower to the project .. (done)")
} else {
// Remove all auto-updated flags
for _, template := range templates {
template.AutoUpdated = false
}
}
}

0 comments on commit d252645

Please sign in to comment.