Skip to content

Commit e640d63

Browse files
committed
feat(config): implement default configurations for common dependencies
1 parent 618137e commit e640d63

File tree

3 files changed

+128
-13
lines changed

3 files changed

+128
-13
lines changed

pkg/config/config.go

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,28 +100,57 @@ type Dependency struct {
100100
Container *Container `yaml:"container"`
101101
}
102102

103-
// UnmarshalYAML is a custom unmarshaler that lets you handle both string and map forms.
103+
// getDefaultConfig retrieves a copy of the default config (if present),
104+
// then applies the provided version to the Image.
105+
func getDefaultConfig(baseName, version string) (*Dependency, bool) {
106+
baseName = strings.ToLower(baseName)
107+
dep, found := defaultConfigs[baseName]
108+
if !found {
109+
return nil, false
110+
}
111+
parts := strings.Split(dep.Image, ":")
112+
if len(parts) == 2 {
113+
dep.Image = parts[0] + ":" + version
114+
} else {
115+
dep.Image += ":" + version
116+
}
117+
return &dep, true
118+
}
119+
120+
// UnmarshalYAML is a custom unmarshaler that lets you handle both string-based
121+
// and map-based dependencies.
104122
func (d *Dependency) UnmarshalYAML(node *yaml.Node) error {
105123
switch node.Tag {
106124
case "!!str":
107125
// If the node is just a string (e.g. "postgres:16"), parse it.
108-
// You can define your own logic here: how to set Name vs. Image, etc.
109-
// For instance, if you want Name = the first part, and Image = the entire string:
110126
value := node.Value
111127
parts := strings.SplitN(value, ":", 2)
112-
if len(parts) > 1 {
113-
d.Name = parts[0]
114-
d.Image = value // e.g. "postgres:16"
128+
129+
if len(parts) == 2 {
130+
// We have a base name + version
131+
base, version := parts[0], parts[1]
132+
if defaultDep, ok := getDefaultConfig(base, version); ok {
133+
*d = *defaultDep
134+
} else {
135+
// fallback for unknown base (e.g., "foobar:1.0")
136+
d.Name = base
137+
d.Image = value
138+
}
115139
} else {
116-
// If there's no colon, define your fallback:
117-
// e.g. "postgres" => use "postgres" for both Name and Image
118-
d.Name = value
119-
d.Image = value
140+
// Only a base name (e.g., "redis")
141+
base := parts[0]
142+
if defaultDep, ok := getDefaultConfig(base, "latest"); ok {
143+
*d = *defaultDep
144+
} else {
145+
// fallback for unknown base (e.g., "foobar")
146+
d.Name = base
147+
d.Image = base
148+
}
120149
}
121150
return nil
122151

123152
case "!!map":
124-
// If the node is a map, just decode into the struct in the usual way.
153+
// If the node is a map, decode into the struct in the usual way.
125154
type dependencyAlias Dependency
126155
var tmp dependencyAlias
127156
if err := node.Decode(&tmp); err != nil {

pkg/config/config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ dependencies:
294294
// Dependency #2: "redis"
295295
// No colon, so we fall back to same name and image
296296
assert.Equal(suite.T(), "redis", config.Dependencies[1].Name)
297-
assert.Equal(suite.T(), "redis", config.Dependencies[1].Image)
297+
assert.Equal(suite.T(), "redis:latest", config.Dependencies[1].Image)
298298
}
299299

300300
func (suite *ConfigTestSuite) TestParseConfig_MixedDependencies() {
@@ -339,5 +339,5 @@ dependencies:
339339
// Dependency #3: "elasticsearch" (no colon)
340340
// We expect same name / image
341341
assert.Equal(suite.T(), "elasticsearch", config.Dependencies[2].Name)
342-
assert.Equal(suite.T(), "elasticsearch", config.Dependencies[2].Image)
342+
assert.Equal(suite.T(), "elasticsearch:latest", config.Dependencies[2].Image)
343343
}

pkg/config/default_configs.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package config
2+
3+
// defaultConfigs holds "base name" → default configuration
4+
// (image, ports, volumes, environment variables, container settings, etc.).
5+
var defaultConfigs = map[string]Dependency{
6+
"mysql": {
7+
Name: "mysql",
8+
Image: "mysql:latest",
9+
Ports: []int{3306},
10+
Volumes: []string{"mysql_data:/var/lib/mysql"},
11+
Env: []string{
12+
"MYSQL_ROOT_PASSWORD=production-secret",
13+
},
14+
},
15+
"postgres": {
16+
Name: "postgres",
17+
Image: "postgres:latest",
18+
Ports: []int{5432},
19+
Volumes: []string{"postgres_data:/var/lib/postgresql/data"},
20+
Env: []string{
21+
"POSTGRES_PASSWORD=production-secret",
22+
},
23+
},
24+
"postgresql": {
25+
Name: "postgres",
26+
Image: "postgres:latest",
27+
Ports: []int{5432},
28+
Volumes: []string{"postgres_data:/var/lib/postgresql/data"},
29+
Env: []string{
30+
"POSTGRES_PASSWORD=production-secret",
31+
},
32+
},
33+
"elasticsearch": {
34+
Name: "elasticsearch",
35+
Image: "elasticsearch:latest",
36+
Ports: []int{9200, 9300},
37+
Volumes: []string{"es_data:/usr/share/elasticsearch/data"},
38+
Env: []string{
39+
"discovery.type=single-node",
40+
},
41+
Container: &Container{
42+
ULimits: []ULimit{
43+
{
44+
Name: "nofile",
45+
Soft: 65535,
46+
Hard: 65535,
47+
},
48+
},
49+
},
50+
},
51+
"mongodb": {
52+
Name: "mongodb",
53+
Image: "mongo:latest",
54+
Ports: []int{27017},
55+
Volumes: []string{"mongo_data:/data/db"},
56+
Env: []string{
57+
"MONGO_INITDB_ROOT_USERNAME=root",
58+
"MONGO_INITDB_ROOT_PASSWORD=production-secret",
59+
},
60+
},
61+
"redis": {
62+
Name: "redis",
63+
Image: "redis:latest",
64+
Ports: []int{6379},
65+
Volumes: []string{"redis_data:/data"},
66+
},
67+
"memcached": {
68+
Name: "memcached",
69+
Image: "memcached:latest",
70+
Ports: []int{11211},
71+
},
72+
"rabbitmq": {
73+
Name: "rabbitmq",
74+
Image: "rabbitmq:latest",
75+
Ports: []int{5672, 15672}, // main + management
76+
Container: &Container{
77+
ULimits: []ULimit{
78+
{
79+
Name: "nofile",
80+
Soft: 65535,
81+
Hard: 65535,
82+
},
83+
},
84+
},
85+
},
86+
}

0 commit comments

Comments
 (0)