mirror of
https://github.com/docker/compose.git
synced 2025-09-23 09:47:49 +02:00
Replicate docker-compose file loading
Skeletton to produce helm chart by internal conversion Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
fea9697d77
commit
ba43317862
23
compose/docker.go
Normal file
23
compose/docker.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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()
|
||||||
|
}
|
38
compose/errors.go
Normal file
38
compose/errors.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func combine(errors []error) error {
|
||||||
|
if len(errors) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(errors) == 1 {
|
||||||
|
return errors[0]
|
||||||
|
}
|
||||||
|
err := combinedError{}
|
||||||
|
for _, e := range errors {
|
||||||
|
if c, ok := e.(combinedError); ok {
|
||||||
|
err.errors = append(err.errors, c.errors...)
|
||||||
|
} else {
|
||||||
|
err.errors = append(err.errors, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combinedError{errors}
|
||||||
|
}
|
||||||
|
|
||||||
|
type combinedError struct {
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c combinedError) Error() string {
|
||||||
|
points := make([]string, len(c.errors))
|
||||||
|
for i, err := range c.errors {
|
||||||
|
points[i] = fmt.Sprintf("* %s", err.Error())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%d errors occurred:\n\t%s",
|
||||||
|
len(c.errors), strings.Join(points, "\n\t"))
|
||||||
|
}
|
17
compose/labels.go
Normal file
17
compose/labels.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package compose
|
||||||
|
|
||||||
|
const (
|
||||||
|
LABEL_DOCKER_COMPOSE_PREFIX = "com.docker.compose"
|
||||||
|
LABEL_SERVICE = LABEL_DOCKER_COMPOSE_PREFIX + ".service"
|
||||||
|
LABEL_VERSION = LABEL_DOCKER_COMPOSE_PREFIX + ".version"
|
||||||
|
LABEL_CONTAINER_NUMBER = LABEL_DOCKER_COMPOSE_PREFIX + ".container-number"
|
||||||
|
LABEL_ONE_OFF = LABEL_DOCKER_COMPOSE_PREFIX + ".oneoff"
|
||||||
|
LABEL_NETWORK = LABEL_DOCKER_COMPOSE_PREFIX + ".network"
|
||||||
|
LABEL_SLUG = LABEL_DOCKER_COMPOSE_PREFIX + ".slug"
|
||||||
|
LABEL_VOLUME = LABEL_DOCKER_COMPOSE_PREFIX + ".volume"
|
||||||
|
LABEL_CONFIG_HASH = LABEL_DOCKER_COMPOSE_PREFIX + ".config-hash"
|
||||||
|
LABEL_PROJECT = LABEL_DOCKER_COMPOSE_PREFIX + ".project"
|
||||||
|
LABEL_WORKING_DIR = LABEL_DOCKER_COMPOSE_PREFIX + ".working_dir"
|
||||||
|
LABEL_CONFIG_FILES = LABEL_DOCKER_COMPOSE_PREFIX + ".config_files"
|
||||||
|
LABEL_ENVIRONMENT_FILE = LABEL_DOCKER_COMPOSE_PREFIX + ".environment_file"
|
||||||
|
)
|
26
compose/project.go
Normal file
26
compose/project.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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
|
||||||
|
}
|
96
helm/output.go
Normal file
96
helm/output.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package helm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Write(project string, objects map[string]runtime.Object, target string) error {
|
||||||
|
out := Outputer{ target }
|
||||||
|
|
||||||
|
if err := out.Write("README.md", []byte("This chart was created by converting a Compose file")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chart := `name: {{.Name}}
|
||||||
|
description: A generated Helm Chart for {{.Name}} from Skippbox Kompose
|
||||||
|
version: 0.0.1
|
||||||
|
apiVersion: v1
|
||||||
|
keywords:
|
||||||
|
- {{.Name}}
|
||||||
|
sources:
|
||||||
|
home:
|
||||||
|
`
|
||||||
|
|
||||||
|
t, err := template.New("ChartTmpl").Parse(chart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
type ChartDetails struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
var chartData bytes.Buffer
|
||||||
|
_ = t.Execute(&chartData, ChartDetails{project})
|
||||||
|
|
||||||
|
|
||||||
|
if err := out.Write("Chart.yaml", chartData.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, o := range objects {
|
||||||
|
j, err := json.Marshal(o)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := jsonToYaml(j, 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := out.Write(filepath.Join("templates", name), b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Outputer struct {
|
||||||
|
Dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Outputer) Write(path string, content []byte) error {
|
||||||
|
out := filepath.Join(o.Dir, path)
|
||||||
|
os.MkdirAll(filepath.Dir(out), 0744)
|
||||||
|
return ioutil.WriteFile(out, content, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert JSON to YAML.
|
||||||
|
func jsonToYaml(j []byte, spaces int) ([]byte, error) {
|
||||||
|
// Convert the JSON to an object.
|
||||||
|
var jsonObj interface{}
|
||||||
|
// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
|
||||||
|
// Go JSON library doesn't try to pick the right number type (int, float,
|
||||||
|
// etc.) when unmarshling to interface{}, it just picks float64
|
||||||
|
// universally. go-yaml does go through the effort of picking the right
|
||||||
|
// number type, so we can preserve number type throughout this process.
|
||||||
|
err := yaml.Unmarshal(j, &jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
encoder := yaml.NewEncoder(&b)
|
||||||
|
encoder.SetIndent(spaces)
|
||||||
|
if err := encoder.Encode(jsonObj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
|
||||||
|
// Marshal this object into YAML.
|
||||||
|
// return yaml.Marshal(jsonObj)
|
||||||
|
}
|
72
transform/kube.go
Normal file
72
transform/kube.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package transform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/docker/helm-prototype/pkg/compose"
|
||||||
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
core "k8s.io/api/core/v1"
|
||||||
|
apps "k8s.io/api/apps/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MapToKubernetesObjects(model *compose.Project) (map[string]runtime.Object, error) {
|
||||||
|
objects := map[string]runtime.Object{}
|
||||||
|
for _, service := range model.Services {
|
||||||
|
objects[fmt.Sprintf("%s-service.yaml", service.Name)] = mapToService(service)
|
||||||
|
objects[fmt.Sprintf("%s-deployment.yaml", service.Name)] = mapToDeployment(service)
|
||||||
|
for _, vol := range service.Volumes {
|
||||||
|
if vol.Type == "volume" {
|
||||||
|
objects[fmt.Sprintf("%s-persistentvolumeclain.yaml", service.Name)] = mapToPVC(service, vol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToService(service types.ServiceConfig) *core.Service {
|
||||||
|
return &core.Service{
|
||||||
|
ObjectMeta: meta.ObjectMeta{
|
||||||
|
Name: service.Name,
|
||||||
|
},
|
||||||
|
Spec: core.ServiceSpec{
|
||||||
|
Selector: map[string]string{"com.docker.compose.service": service.Name},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToDeployment(service types.ServiceConfig) *apps.Deployment {
|
||||||
|
return &apps.Deployment{
|
||||||
|
ObjectMeta: meta.ObjectMeta{
|
||||||
|
Name: service.Name,
|
||||||
|
Labels: map[string]string{"com.docker.compose.service": service.Name},
|
||||||
|
},
|
||||||
|
Spec: apps.DeploymentSpec{
|
||||||
|
Template: core.PodTemplateSpec{
|
||||||
|
ObjectMeta: meta.ObjectMeta{
|
||||||
|
Labels: map[string]string{"com.docker.compose.service": service.Name},
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
Containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: service.Name,
|
||||||
|
Image: service.Image,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToPVC(service types.ServiceConfig, vol types.ServiceVolumeConfig) runtime.Object {
|
||||||
|
return &core.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: meta.ObjectMeta{
|
||||||
|
Name: vol.Source,
|
||||||
|
Labels: map[string]string{"com.docker.compose.service": service.Name},
|
||||||
|
},
|
||||||
|
Spec: core.PersistentVolumeClaimSpec{
|
||||||
|
VolumeName: vol.Source,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user