1
0
mirror of https://github.com/rancher/os.git synced 2025-06-24 14:01:34 +00:00
os/compose/project.go

301 lines
7.6 KiB
Go
Raw Normal View History

2015-08-04 21:45:38 +00:00
package compose
import (
2016-03-01 03:29:07 +00:00
"fmt"
2016-05-24 00:21:28 +00:00
"golang.org/x/net/context"
2015-08-04 21:45:38 +00:00
log "github.com/Sirupsen/logrus"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
2015-08-04 21:45:38 +00:00
"github.com/docker/libcompose/cli/logger"
2016-05-24 00:21:28 +00:00
composeConfig "github.com/docker/libcompose/config"
2015-08-04 21:45:38 +00:00
"github.com/docker/libcompose/docker"
2016-05-24 00:21:28 +00:00
composeClient "github.com/docker/libcompose/docker/client"
2015-08-04 21:45:38 +00:00
"github.com/docker/libcompose/project"
2016-05-24 00:21:28 +00:00
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
"github.com/rancher/os/config"
rosDocker "github.com/rancher/os/docker"
"github.com/rancher/os/util"
"github.com/rancher/os/util/network"
2015-08-04 21:45:38 +00:00
)
2016-05-24 00:21:28 +00:00
func CreateService(cfg *config.CloudConfig, name string, serviceConfig *composeConfig.ServiceConfigV1) (project.Service, error) {
2015-08-04 21:45:38 +00:00
if cfg == nil {
var err error
cfg, err = config.LoadConfig()
if err != nil {
return nil, err
}
}
2016-05-24 00:21:28 +00:00
p, err := CreateServiceSet("once", cfg, map[string]*composeConfig.ServiceConfigV1{
2015-08-04 21:45:38 +00:00
name: serviceConfig,
})
if err != nil {
return nil, err
}
return p.CreateService(name)
}
2016-05-24 00:21:28 +00:00
func CreateServiceSet(name string, cfg *config.CloudConfig, configs map[string]*composeConfig.ServiceConfigV1) (*project.Project, error) {
p, err := newProject(name, cfg, nil)
2015-08-04 21:45:38 +00:00
if err != nil {
return nil, err
}
addServices(p, map[interface{}]interface{}{}, configs)
2015-08-04 21:45:38 +00:00
2016-02-26 20:04:32 +00:00
return p, nil
}
2016-05-24 00:21:28 +00:00
func RunServiceSet(name string, cfg *config.CloudConfig, configs map[string]*composeConfig.ServiceConfigV1) (*project.Project, error) {
2016-02-26 20:04:32 +00:00
p, err := CreateServiceSet(name, cfg, configs)
if err != nil {
return nil, err
}
2016-05-24 00:21:28 +00:00
return p, p.Up(context.Background(), options.Up{})
2015-08-04 21:45:38 +00:00
}
func GetProject(cfg *config.CloudConfig, networkingAvailable bool) (*project.Project, error) {
return newCoreServiceProject(cfg, networkingAvailable)
}
2016-05-24 00:21:28 +00:00
func newProject(name string, cfg *config.CloudConfig, environmentLookup composeConfig.EnvironmentLookup) (*project.Project, error) {
clientFactory, err := rosDocker.NewClientFactory(composeClient.Options{})
2015-08-04 21:45:38 +00:00
if err != nil {
return nil, err
}
if environmentLookup == nil {
environmentLookup = rosDocker.NewConfigEnvironment(cfg)
}
2015-08-04 21:45:38 +00:00
serviceFactory := &rosDocker.ServiceFactory{
Deps: map[string][]string{},
}
context := &docker.Context{
ClientFactory: clientFactory,
Context: project.Context{
ProjectName: name,
EnvironmentLookup: environmentLookup,
2015-08-04 21:45:38 +00:00
ServiceFactory: serviceFactory,
LoggerFactory: logger.NewColorLoggerFactory(),
},
}
serviceFactory.Context = context
2016-05-24 00:21:28 +00:00
return docker.NewProject(context, &composeConfig.ParseOptions{
Interpolate: true,
Validate: false,
Preprocess: preprocessServiceMap,
})
}
func preprocessServiceMap(serviceMap composeConfig.RawServiceMap) (composeConfig.RawServiceMap, error) {
newServiceMap := make(composeConfig.RawServiceMap)
for k, v := range serviceMap {
newServiceMap[k] = make(composeConfig.RawService)
for k2, v2 := range v {
if k2 == "environment" || k2 == "labels" {
newServiceMap[k][k2] = preprocess(v2, true)
} else {
newServiceMap[k][k2] = preprocess(v2, false)
}
}
}
return newServiceMap, nil
2015-08-04 21:45:38 +00:00
}
2016-05-24 00:21:28 +00:00
func preprocess(item interface{}, replaceTypes bool) interface{} {
switch typedDatas := item.(type) {
case map[interface{}]interface{}:
newMap := make(map[interface{}]interface{})
for key, value := range typedDatas {
newMap[key] = preprocess(value, replaceTypes)
}
return newMap
case []interface{}:
// newArray := make([]interface{}, 0) will cause golint to complain
var newArray []interface{}
newArray = make([]interface{}, 0)
for _, value := range typedDatas {
newArray = append(newArray, preprocess(value, replaceTypes))
}
return newArray
default:
if replaceTypes {
return fmt.Sprint(item)
}
return item
}
}
func addServices(p *project.Project, enabled map[interface{}]interface{}, configs map[string]*composeConfig.ServiceConfigV1) map[interface{}]interface{} {
serviceConfigsV2, _ := composeConfig.ConvertServices(configs)
2015-08-04 21:45:38 +00:00
// Note: we ignore errors while loading services
unchanged := true
2016-05-24 00:21:28 +00:00
for name, serviceConfig := range serviceConfigsV2 {
hash := composeConfig.GetServiceHash(name, serviceConfig)
2015-08-04 21:45:38 +00:00
if enabled[name] == hash {
continue
}
if err := p.AddConfig(name, serviceConfig); err != nil {
log.Infof("Failed loading service %s", name)
continue
}
if unchanged {
enabled = util.MapCopy(enabled)
unchanged = false
}
2015-08-04 21:45:38 +00:00
enabled[name] = hash
}
return enabled
2015-08-04 21:45:38 +00:00
}
func adjustContainerNames(m map[interface{}]interface{}) map[interface{}]interface{} {
for k, v := range m {
if k, ok := k.(string); ok {
if v, ok := v.(map[interface{}]interface{}); ok {
if _, ok := v["container_name"]; !ok {
v["container_name"] = k
}
}
}
}
return m
}
2016-05-24 00:21:28 +00:00
func newCoreServiceProject(cfg *config.CloudConfig, useNetwork bool) (*project.Project, error) {
projectEvents := make(chan events.Event)
enabled := map[interface{}]interface{}{}
2015-08-04 21:45:38 +00:00
environmentLookup := rosDocker.NewConfigEnvironment(cfg)
p, err := newProject("os", cfg, environmentLookup)
2015-08-04 21:45:38 +00:00
if err != nil {
return nil, err
}
p.AddListener(project.NewDefaultListener(p))
p.AddListener(projectEvents)
p.ReloadCallback = func() error {
var err error
cfg, err = config.LoadConfig()
2015-08-04 21:45:38 +00:00
if err != nil {
return err
}
environmentLookup.SetConfig(cfg)
enabled = addServices(p, enabled, cfg.Rancher.Services)
2015-08-04 21:45:38 +00:00
for service, serviceEnabled := range cfg.Rancher.ServicesInclude {
if _, ok := enabled[service]; ok || !serviceEnabled {
2015-08-04 21:45:38 +00:00
continue
}
bytes, err := network.LoadServiceResource(service, useNetwork, cfg)
2015-08-04 21:45:38 +00:00
if err != nil {
if err == network.ErrNoNetwork {
2015-08-04 21:45:38 +00:00
log.Debugf("Can not load %s, networking not enabled", service)
} else {
log.Errorf("Failed to load %s : %v", service, err)
}
continue
}
m := map[interface{}]interface{}{}
if err := yaml.Unmarshal(bytes, &m); err != nil {
log.Errorf("Failed to parse YAML configuration: %s : %v", service, err)
continue
}
bytes, err = yaml.Marshal(adjustContainerNames(config.StringifyValues(m)))
if err != nil {
log.Errorf("Failed to marshal YAML configuration: %s : %v", service, err)
continue
}
2015-08-04 21:45:38 +00:00
err = p.Load(bytes)
if err != nil {
log.Errorf("Failed to load %s : %v", service, err)
continue
}
enabled[service] = service
}
return nil
}
go func() {
for event := range projectEvents {
2016-05-24 00:21:28 +00:00
if event.EventType == events.ContainerStarted && event.ServiceName == "ntp" {
useNetwork = true
2015-08-04 21:45:38 +00:00
}
}
}()
err = p.ReloadCallback()
if err != nil {
log.Errorf("Failed to reload os: %v", err)
return nil, err
}
return p, nil
}
2016-03-01 03:29:07 +00:00
func StageServices(cfg *config.CloudConfig, services ...string) error {
p, err := newProject("stage-services", cfg, nil)
2016-03-01 03:29:07 +00:00
if err != nil {
return err
}
for _, service := range services {
bytes, err := network.LoadServiceResource(service, true, cfg)
2016-03-01 03:29:07 +00:00
if err != nil {
return fmt.Errorf("Failed to load %s : %v", service, err)
}
m := map[interface{}]interface{}{}
if err := yaml.Unmarshal(bytes, &m); err != nil {
return fmt.Errorf("Failed to parse YAML configuration: %s : %v", service, err)
}
bytes, err = yaml.Marshal(config.StringifyValues(m))
if err != nil {
return fmt.Errorf("Failed to marshal YAML configuration: %s : %v", service, err)
2016-03-01 03:29:07 +00:00
}
err = p.Load(bytes)
if err != nil {
return fmt.Errorf("Failed to load %s : %v", service, err)
2016-03-01 03:29:07 +00:00
}
}
// Reduce service configurations to just image and labels
2016-05-24 00:21:28 +00:00
for _, serviceName := range p.ServiceConfigs.Keys() {
serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
p.ServiceConfigs.Add(serviceName, &composeConfig.ServiceConfig{
2016-03-01 03:29:07 +00:00
Image: serviceConfig.Image,
Labels: serviceConfig.Labels,
2016-05-24 00:21:28 +00:00
})
2016-03-01 03:29:07 +00:00
}
2016-05-24 00:21:28 +00:00
return p.Pull(context.Background())
2016-03-01 03:29:07 +00:00
}