mirror of https://github.com/docker/compose.git
project restructure
Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
parent
4ffc393909
commit
ce3e4d7717
|
@ -0,0 +1,96 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
registry "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/helm-prototype/pkg/compose/internal/convert"
|
||||
"github.com/docker/helm-prototype/pkg/compose/internal/helm"
|
||||
utils "github.com/docker/helm-prototype/pkg/compose/internal/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
Client client.APIClient
|
||||
RegistryClient registry.RegistryClient
|
||||
ConfigFile *configfile.ConfigFile
|
||||
Stdout *streams.Out
|
||||
)
|
||||
|
||||
func WithDockerCli(cli command.Cli) {
|
||||
Client = cli.Client()
|
||||
RegistryClient = cli.RegistryClient(false)
|
||||
ConfigFile = cli.ConfigFile()
|
||||
Stdout = cli.Out()
|
||||
}
|
||||
|
||||
// Orchestrator is "kubernetes" or "swarm"
|
||||
type Orchestrator string
|
||||
|
||||
const (
|
||||
// Kubernetes specifies to use kubernetes.
|
||||
Kubernetes Orchestrator = "kubernetes"
|
||||
// Swarm specifies to use Docker swarm.
|
||||
Swarm Orchestrator = "swarm"
|
||||
)
|
||||
|
||||
type ProjectOptions struct {
|
||||
ConfigPaths []string
|
||||
Name string
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Config *types.Config
|
||||
ProjectDir string
|
||||
Name string `yaml:"-" json:"-"`
|
||||
Orchestrator Orchestrator
|
||||
}
|
||||
|
||||
func NewProject(config types.ConfigDetails, name string) (*Project, error) {
|
||||
model, err := loader.Load(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := Project{
|
||||
Config: model,
|
||||
ProjectDir: config.WorkingDir,
|
||||
Name: name,
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// projectFromOptions load a compose project based on command line options
|
||||
func ProjectFromOptions(options *ProjectOptions) (*Project, error) {
|
||||
workingDir, configs, err := utils.GetConfigs(
|
||||
options.Name,
|
||||
options.ConfigPaths,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewProject(types.ConfigDetails{
|
||||
WorkingDir: workingDir,
|
||||
ConfigFiles: configs,
|
||||
Environment: utils.Environment(),
|
||||
}, options.Name)
|
||||
}
|
||||
|
||||
func (p *Project) GenerateCharts(path string) error {
|
||||
objects, err := convert.MapToKubernetesObjects(p.Config, p.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = helm.Write(p.Name, objects, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *Project) InstallCommand(options *ProjectOptions) error {
|
||||
return nil
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
registry "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
var (
|
||||
Client client.APIClient
|
||||
RegistryClient registry.RegistryClient
|
||||
ConfigFile *configfile.ConfigFile
|
||||
Stdout *streams.Out
|
||||
)
|
||||
|
||||
func WithDockerCli(cli command.Cli) {
|
||||
Client = cli.Client()
|
||||
RegistryClient = cli.RegistryClient(false)
|
||||
ConfigFile = cli.ConfigFile()
|
||||
Stdout = cli.Out()
|
||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/helm-prototype/pkg/compose"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -14,19 +13,19 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func MapToKubernetesObjects(model *compose.Project) (map[string]runtime.Object, error) {
|
||||
func MapToKubernetesObjects(model *types.Config, name string) (map[string]runtime.Object, error) {
|
||||
objects := map[string]runtime.Object{}
|
||||
|
||||
for _, service := range model.Services {
|
||||
objects[fmt.Sprintf("%s-service.yaml", service.Name)] = mapToService(model, service)
|
||||
if service.Deploy != nil && service.Deploy.Mode == "global" {
|
||||
daemonset, err := mapToDaemonset(service, model)
|
||||
daemonset, err := mapToDaemonset(service, model, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects[fmt.Sprintf("%s-daemonset.yaml", service.Name)] = daemonset
|
||||
} else {
|
||||
deployment, err := mapToDeployment(service, model)
|
||||
deployment, err := mapToDeployment(service, model, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -41,7 +40,7 @@ func MapToKubernetesObjects(model *compose.Project) (map[string]runtime.Object,
|
|||
return objects, nil
|
||||
}
|
||||
|
||||
func mapToService(model *compose.Project, service types.ServiceConfig) *core.Service {
|
||||
func mapToService(model *types.Config, service types.ServiceConfig) *core.Service {
|
||||
ports := []core.ServicePort{}
|
||||
for _, p := range service.Ports {
|
||||
ports = append(ports,
|
||||
|
@ -69,7 +68,7 @@ func mapToService(model *compose.Project, service types.ServiceConfig) *core.Ser
|
|||
}
|
||||
}
|
||||
|
||||
func mapServiceToServiceType(service types.ServiceConfig, model *compose.Project) core.ServiceType {
|
||||
func mapServiceToServiceType(service types.ServiceConfig, model *types.Config) core.ServiceType {
|
||||
serviceType := core.ServiceTypeClusterIP
|
||||
if len(service.Networks) == 0 {
|
||||
// service is implicitly attached to "default" network
|
||||
|
@ -88,10 +87,10 @@ func mapServiceToServiceType(service types.ServiceConfig, model *compose.Project
|
|||
return serviceType
|
||||
}
|
||||
|
||||
func mapToDeployment(service types.ServiceConfig, model *compose.Project) (*apps.Deployment, error) {
|
||||
func mapToDeployment(service types.ServiceConfig, model *types.Config, name string) (*apps.Deployment, error) {
|
||||
labels := map[string]string{
|
||||
"com.docker.compose.service": service.Name,
|
||||
"com.docker.compose.project": model.Name,
|
||||
"com.docker.compose.project": name,
|
||||
}
|
||||
podTemplate, err := toPodTemplate(service, labels, model)
|
||||
if err != nil {
|
||||
|
@ -120,10 +119,10 @@ func mapToDeployment(service types.ServiceConfig, model *compose.Project) (*apps
|
|||
}, nil
|
||||
}
|
||||
|
||||
func mapToDaemonset(service types.ServiceConfig, model *compose.Project) (*apps.DaemonSet, error) {
|
||||
func mapToDaemonset(service types.ServiceConfig, model *types.Config, name string) (*apps.DaemonSet, error) {
|
||||
labels := map[string]string{
|
||||
"com.docker.compose.service": service.Name,
|
||||
"com.docker.compose.project": model.Name,
|
||||
"com.docker.compose.project": name,
|
||||
}
|
||||
podTemplate, err := toPodTemplate(service, labels, model)
|
||||
if err != nil {
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/helm-prototype/pkg/compose"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
|
@ -17,7 +16,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func toPodTemplate(serviceConfig types.ServiceConfig, labels map[string]string, model *compose.Project) (apiv1.PodTemplateSpec, error) {
|
||||
func toPodTemplate(serviceConfig types.ServiceConfig, labels map[string]string, model *types.Config) (apiv1.PodTemplateSpec, error) {
|
||||
tpl := apiv1.PodTemplateSpec{}
|
||||
nodeAffinity, err := toNodeAffinity(serviceConfig.Deploy)
|
||||
if err != nil {
|
|
@ -8,13 +8,12 @@ import (
|
|||
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/helm-prototype/pkg/compose"
|
||||
"github.com/stretchr/testify/assert"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
func loadYAML(yaml string) (*compose.Project, error) {
|
||||
func loadYAML(yaml string) (*loader.Config, error) {
|
||||
dict, err := loader.ParseYAML([]byte(yaml))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -23,12 +22,17 @@ func loadYAML(yaml string) (*compose.Project, error) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return compose.NewProject(types.ConfigDetails{
|
||||
WorkingDir: workingDir,
|
||||
ConfigFiles: []types.ConfigFile{
|
||||
{Filename: "compose.yaml", Config: dict},
|
||||
},
|
||||
}, "test")
|
||||
configs := ConfigFiles: []types.ConfigFile{
|
||||
{Filename: "compose.yaml", Config: dict},
|
||||
},
|
||||
|
||||
config := types.ConfigDetails{
|
||||
WorkingDir: workingDir,
|
||||
ConfigFiles: configs,
|
||||
Environment: utils.Environment(),
|
||||
}
|
||||
model, err := loader.Load(config)
|
||||
return model
|
||||
}
|
||||
|
||||
func podTemplate(t *testing.T, yaml string) apiv1.PodTemplateSpec {
|
||||
|
@ -38,11 +42,12 @@ func podTemplate(t *testing.T, yaml string) apiv1.PodTemplateSpec {
|
|||
}
|
||||
|
||||
func podTemplateWithError(yaml string) (apiv1.PodTemplateSpec, error) {
|
||||
project, err := loadYAML(yaml)
|
||||
model, err := loadYAML(yaml)
|
||||
if err != nil {
|
||||
return apiv1.PodTemplateSpec{}, err
|
||||
}
|
||||
return toPodTemplate(project.Services[0], nil, project)
|
||||
|
||||
return toPodTemplate(model.Services[0], nil, model)
|
||||
}
|
||||
|
||||
func TestToPodWithDockerSocket(t *testing.T) {
|
|
@ -7,7 +7,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/helm-prototype/pkg/compose"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
|
@ -30,7 +29,7 @@ func hasPersistentVolumes(s types.ServiceConfig) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func toVolumeSpecs(s types.ServiceConfig, model *compose.Project) ([]volumeSpec, error) {
|
||||
func toVolumeSpecs(s types.ServiceConfig, model *types.Config) ([]volumeSpec, error) {
|
||||
var specs []volumeSpec
|
||||
for i, m := range s.Volumes {
|
||||
var source *apiv1.VolumeSource
|
||||
|
@ -114,7 +113,7 @@ func or(v string, defaultValue string) string {
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
func toVolumeMounts(s types.ServiceConfig, model *compose.Project) ([]apiv1.VolumeMount, error) {
|
||||
func toVolumeMounts(s types.ServiceConfig, model *types.Config) ([]apiv1.VolumeMount, error) {
|
||||
var mounts []apiv1.VolumeMount
|
||||
specs, err := toVolumeSpecs(s, model)
|
||||
if err != nil {
|
||||
|
@ -126,7 +125,7 @@ func toVolumeMounts(s types.ServiceConfig, model *compose.Project) ([]apiv1.Volu
|
|||
return mounts, nil
|
||||
}
|
||||
|
||||
func toVolumes(s types.ServiceConfig, model *compose.Project) ([]apiv1.Volume, error) {
|
||||
func toVolumes(s types.ServiceConfig, model *types.Config) ([]apiv1.Volume, error) {
|
||||
var volumes []apiv1.Volume
|
||||
specs, err := toVolumeSpecs(s, model)
|
||||
if err != nil {
|
|
@ -0,0 +1,34 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
type KubeConfig struct {
|
||||
namespace string
|
||||
config genericclioptions.RESTClientGetter
|
||||
configOnce sync.Once
|
||||
|
||||
// KubeConfig is the path to the kubeconfig file
|
||||
KubeConfig string
|
||||
// KubeContext is the name of the kubeconfig context.
|
||||
KubeContext string
|
||||
// Bearer KubeToken used for authentication
|
||||
KubeToken string
|
||||
// Kubernetes API Server Endpoint for authentication
|
||||
KubeAPIServer string
|
||||
}
|
||||
|
||||
func New() *KubeConfig {
|
||||
|
||||
env := KubeConfig{
|
||||
namespace: "",
|
||||
KubeContext: os.Getenv("COMPOSE_KUBECONTEXT"),
|
||||
KubeToken: os.Getenv("COMPOSE_KUBETOKEN"),
|
||||
KubeAPIServer: os.Getenv("COMPOSE_KUBEAPISERVER"),
|
||||
}
|
||||
return &env
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/prometheus/common/log"
|
||||
)
|
||||
|
||||
var SupportedFilenames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
|
||||
|
||||
func GetConfigs(name string, configPaths []string) (string, []types.ConfigFile, error) {
|
||||
configPath, err := getConfigPaths(configPaths)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
name = os.Getenv("COMPOSE_PROJECT_NAME")
|
||||
}
|
||||
|
||||
workingDir := filepath.Dir(configPath[0])
|
||||
|
||||
if name == "" {
|
||||
r := regexp.MustCompile(`[^a-z0-9\\-_]+`)
|
||||
name = r.ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "")
|
||||
}
|
||||
|
||||
configs, err := parseConfigs(configPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return workingDir, configs, nil
|
||||
}
|
||||
|
||||
func getConfigPaths(configPaths []string) ([]string, error) {
|
||||
paths := []string{}
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(configPaths) != 0 {
|
||||
for _, f := range configPaths {
|
||||
if f == "-" {
|
||||
paths = append(paths, f)
|
||||
continue
|
||||
}
|
||||
if !filepath.IsAbs(f) {
|
||||
f = filepath.Join(pwd, f)
|
||||
}
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, f)
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
sep := os.Getenv("COMPOSE_FILE_SEPARATOR")
|
||||
if sep == "" {
|
||||
sep = string(os.PathListSeparator)
|
||||
}
|
||||
f := os.Getenv("COMPOSE_FILE")
|
||||
if f != "" {
|
||||
return strings.Split(f, sep), nil
|
||||
}
|
||||
|
||||
for {
|
||||
candidates := []string{}
|
||||
for _, n := range SupportedFilenames {
|
||||
f := filepath.Join(pwd, n)
|
||||
if _, err := os.Stat(f); err == nil {
|
||||
candidates = append(candidates, f)
|
||||
}
|
||||
}
|
||||
if len(candidates) > 0 {
|
||||
winner := candidates[0]
|
||||
if len(candidates) > 1 {
|
||||
log.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))
|
||||
log.Warnf("Using %s\n", winner)
|
||||
}
|
||||
return []string{winner}, nil
|
||||
}
|
||||
parent := filepath.Dir(pwd)
|
||||
if parent == pwd {
|
||||
return nil, fmt.Errorf("Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?")
|
||||
}
|
||||
pwd = parent
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
|
||||
files := []types.ConfigFile{}
|
||||
for _, f := range configPaths {
|
||||
var (
|
||||
b []byte
|
||||
err error
|
||||
)
|
||||
if f == "-" {
|
||||
b, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err = ioutil.ReadFile(f)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := loader.ParseYAML(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, types.ConfigFile{Filename: f, Config: config})
|
||||
}
|
||||
return files, nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Environment() map[string]string {
|
||||
return getAsEqualsMap(os.Environ())
|
||||
}
|
||||
|
||||
// getAsEqualsMap split key=value formatted strings into a key : value map
|
||||
func getAsEqualsMap(em []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, v := range em {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
m[kv[0]] = kv[1]
|
||||
}
|
||||
return m
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package compose
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func combine(errors []error) error {
|
||||
func CombineErrors(errors []error) error {
|
||||
if len(errors) == 0 {
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package compose
|
||||
package utils
|
||||
|
||||
const (
|
||||
LabelDockerComposePrefix = "com.docker.compose"
|
|
@ -1,26 +0,0 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
types.Config
|
||||
projectDir string
|
||||
Name string `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func NewProject(config types.ConfigDetails, name string) (*Project, error) {
|
||||
model, err := loader.Load(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := Project{
|
||||
Config: *model,
|
||||
projectDir: config.WorkingDir,
|
||||
Name: name,
|
||||
}
|
||||
return &p, nil
|
||||
}
|
Loading…
Reference in New Issue