-
Notifications
You must be signed in to change notification settings - Fork 63
/
project.go
118 lines (97 loc) · 3.02 KB
/
project.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2023, Unikraft GmbH and The KraftKit Authors.
// Licensed under the BSD-3-Clause License (the "License").
// You may not use this file except in compliance with the License.
// Package compose provides primitives for running Unikraft applications
// via the Compose specification.
package compose
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"kraftkit.sh/log"
mplatform "kraftkit.sh/machine/platform"
ukarch "kraftkit.sh/unikraft/arch"
)
type Project struct {
*types.Project `json:"project"` // The underlying compose-go project
}
// DefaultFileNames is a list of default compose file names to look for
var DefaultFileNames = []string{
"docker-compose.yml",
"docker-compose.yaml",
"compose.yml",
"compose.yaml",
"Composefile",
}
// NewProjectFromComposeFile loads a compose file and returns a project. If no
// compose file is specified, it will look for one in the current directory.
func NewProjectFromComposeFile(ctx context.Context, workdir, composefile string) (*Project, error) {
if composefile == "" {
for _, file := range DefaultFileNames {
fullpath := filepath.Join(workdir, file)
if _, err := os.Stat(fullpath); err == nil {
log.G(ctx).Debugf("Found compose file: %s", file)
composefile = file
break
}
}
}
if composefile == "" {
return nil, fmt.Errorf("no compose file found")
}
fullpath := filepath.Join(workdir, composefile)
config := types.ConfigDetails{
ConfigFiles: []types.ConfigFile{
{
Filename: fullpath,
},
},
}
project, err := loader.Load(config)
if err != nil {
return nil, err
}
project.ComposeFiles = []string{composefile}
project.WorkingDir = workdir
return &Project{project}, err
}
// Validate performs some early checks on the project to ensure it is valid,
// as well as fill in some unspecified fields.
func (project *Project) Validate(ctx context.Context) error {
// Check that each service has at least an image name or a build context
for _, service := range project.Services {
if service.Image == "" && service.Build == nil {
return fmt.Errorf("service %s has neither an image nor a build context", service.Name)
}
}
// If the project has no name, use the directory name
if project.Name == "" {
// Take the last part of the working directory
parts := strings.Split(project.WorkingDir, "/")
project.Name = parts[len(parts)-1]
}
// Fill in any missing image names and prepend the project name
for i, service := range project.Services {
project.Services[i].Name = fmt.Sprint(project.Name, "-", service.Name)
}
// Fill in any missing platforms
for i, service := range project.Services {
if service.Platform == "" {
hostPlatform, _, err := mplatform.Detect(ctx)
if err != nil {
return err
}
hostArch, err := ukarch.HostArchitecture()
if err != nil {
return err
}
project.Services[i].Platform = fmt.Sprint(hostPlatform, "/", hostArch)
}
}
return nil
}