mirror of https://github.com/docker/compose.git
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
|
@ -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()
|
||||
}
|
|
@ -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"))
|
||||
}
|
|
@ -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"
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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…
Reference in New Issue