Пример #1
0
func stopAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	containers, err := m.Containers()
	if err != nil {
		logger.Fatalf("error getting container info: %s", err)
	}
	ids := c.Args()
	if len(ids) == 0 {
		logger.Fatalf("you must specify at least one id")
	}
	for _, cnt := range containers {
		// this can probably be more efficient
		for _, i := range ids {
			if strings.HasPrefix(cnt.ID, i) {
				if err := m.Stop(cnt); err != nil {
					logger.Fatalf("error stopping container: %s\n", err)
				}
				fmt.Printf("stopped %s\n", cnt.ID[:12])
			}
		}
	}
}
Пример #2
0
func infoAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	info, err := m.Info()
	if err != nil {
		logger.Fatalf("error getting cluster info: %s", err)
	}
	cpuPercentage := 0.0
	memPercentage := 0.0
	if info.ReservedCpus > 0.0 && info.Cpus > 0.0 {
		cpuPercentage = (info.ReservedCpus / info.Cpus) * 100
	}
	if info.ReservedMemory > 0.0 && info.Memory > 0.0 {
		memPercentage = (info.ReservedMemory / info.Memory) * 100
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintf(w, "Controller Version: %s\n", info.Version)
	fmt.Fprintf(w, "Cpus: %.2f\n", info.Cpus)
	fmt.Fprintf(w, "Memory: %.2f MB\n", info.Memory)
	fmt.Fprintf(w, "Containers: %d\n", info.ContainerCount)
	fmt.Fprintf(w, "Images: %d\n", info.ImageCount)
	fmt.Fprintf(w, "Engines: %d\n", info.EngineCount)
	fmt.Fprintf(w, "Reserved Cpus: %.2f%% (%.2f)\n", cpuPercentage, info.ReservedCpus)
	fmt.Fprintf(w, "Reserved Memory: %.2f%% (%.2f MB)\n", memPercentage, info.ReservedMemory)
	w.Flush()
}
Пример #3
0
func loginAction(c *cli.Context) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Printf("URL: ")
	ur, err := reader.ReadString('\n')
	if err != nil {
		logger.Fatal(err)
	}
	fmt.Printf("Username: "******"Password: ")
	p := gopass.GetPasswd()
	sUrl := strings.TrimSpace(string(ur[:]))
	username := strings.TrimSpace(string(u[:]))
	pass := strings.TrimSpace(string(p[:]))

	cfg := &client.ShipyardConfig{
		Url:      sUrl,
		Username: username,
	}
	m := client.NewManager(cfg)
	token, err := m.Login(username, pass)
	if err != nil {
		logger.Fatal(err)
	}
	cfg.Token = token.Token
	if err := saveConfig(cfg); err != nil {
		logger.Fatal(err)
	}
}
Пример #4
0
func containersAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	containers, err := m.Containers()
	if err != nil {
		logger.Fatalf("error getting containers: %s", err)
	}
	if len(containers) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "ID\tName\tHost\tState\tPorts")
	for _, c := range containers {
		portDefs := []string{}
		for _, port := range c.Ports {
			p := fmt.Sprintf("%s/%d:%d", port.Proto, port.Port, port.ContainerPort)
			portDefs = append(portDefs, p)
		}
		ports := strings.Join(portDefs, ", ")
		state := "unknown"
		switch c.State.Running {
		case true:
			state = "running"
		case false:
			state = "stopped"
		}
		fmt.Fprintf(w, fmt.Sprintf("%s\t%s\t%s\t%v\t%s\n", c.ID[:12], c.Image.Name, c.Engine.ID, state, ports))
	}
	w.Flush()
}
Пример #5
0
func (m *Manager) init() error {
	var engines []*citadel.Engine
	if m.config.ShipyardUrl != "" {
		cfg := &client.ShipyardConfig{
			Url:        m.config.ShipyardUrl,
			ServiceKey: m.config.ShipyardServiceKey,
		}
		mgr := client.NewManager(cfg)
		eng, err := mgr.Engines()
		if err != nil {
			return err
		}
		for _, e := range eng {
			engines = append(engines, e.Engine)
		}
	} else {
		engines = m.engines
	}
	for _, e := range engines {
		if err := e.Connect(nil); err != nil {
			return err
		}
		logger.Infof("loaded engine: %s", e.ID)
	}
	c, err := cluster.New(scheduler.NewResourceManager(), engines...)
	if err != nil {
		return err
	}
	m.cluster = c
	// register handler
	if err := m.cluster.Events(&EventHandler{Manager: m}); err != nil {
		return err
	}
	return nil
}
Пример #6
0
func eventsAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	events, err := m.Events()
	if err != nil {
		logger.Fatalf("error getting events: %s", err)
	}
	if len(events) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "Time\tMessage\tEngine\tType\tTags")
	for _, e := range events {
		tags := strings.Join(e.Tags, ",")
		message := e.Message
		engine := ""
		if e.Container != nil {
			cntId := e.Container.ID[:12]
			message = fmt.Sprintf("container:%s %s", cntId, e.Message)
		}
		if e.Engine != nil {
			engine = e.Engine.ID
		}
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", e.Time.Format(time.RubyDate), message, engine, e.Type, tags)
	}
	w.Flush()
}
Пример #7
0
func runAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	if c.String("name") == "" {
		logger.Fatal("you must specify an image name")
	}
	env := parseEnvironmentVariables(c.StringSlice("env"))
	ports := parsePorts(c.StringSlice("port"))
	image := &citadel.Image{
		Name:        c.String("name"),
		Cpus:        c.Float64("cpus"),
		Memory:      c.Float64("memory"),
		Hostname:    c.String("hostname"),
		Domainname:  c.String("domain"),
		Labels:      c.StringSlice("label"),
		Args:        c.StringSlice("arg"),
		Environment: env,
		BindPorts:   ports,
		Type:        c.String("type"),
	}
	containers, err := m.Run(image, c.Int("count"), c.Bool("pull"))
	if err != nil {
		logger.Fatalf("error running container: %s\n", err)
	}
	for _, c := range containers {
		fmt.Printf("started %s on %s\n", c.ID[:12], c.Engine.ID)
	}
}
Пример #8
0
func logsAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}

	m := client.NewManager(cfg)
	ids := c.Args()
	if len(ids) == 0 {
		logger.Fatal("you must specify an id")
	}
	id := ids[0]

	container, err := m.Container(id)
	stdout := c.Bool("stdout")
	stderr := c.Bool("stderr")

	// if output not specified, use both
	if stdout == false && stderr == false {
		stdout = true
		stderr = true
	}

	data, err := m.Logs(container, stdout, stderr)
	if err != nil {
		logger.Fatalf("error reading logs: %s", err)
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(data)

	io.Copy(os.Stdout, buf)
}
Пример #9
0
func webhookKeyCreateAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	key, err := m.NewWebhookKey(c.String("image"))
	if err != nil {
		logger.Fatalf("error generating webhook key: %s\n", err)
	}
	fmt.Printf("created key: %s\n", key.Key)
}
Пример #10
0
func serviceKeyCreateAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	key, err := m.NewServiceKey(c.String("description"))
	if err != nil {
		logger.Fatalf("error generating service key: %s\n", err)
	}
	fmt.Printf("created key: %s\n", key.Key)
}
Пример #11
0
func addExtensionAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	extUrl := c.String("url")
	if extUrl == "" {
		logger.Fatalf("you must specify an extension config url")
	}
	env := parseEnvironmentVariables(c.StringSlice("env"))
	args := c.StringSlice("arg")
	resp, err := http.Get(extUrl)
	if err != nil {
		logger.Fatalf("unable to get extension config: %s", err)
	}
	var ext *shipyard.Extension
	if err := json.NewDecoder(resp.Body).Decode(&ext); err != nil {
		logger.Fatalf("error parsing extension config: %s", err, err)
	}
	fmt.Printf("configuring %s (%s for more info)\n", ext.Name, ext.Url)
	// check for configuration
	for _, pe := range ext.Config.PromptEnvironment {
		fmt.Printf("enter value for container environment variable %s: ", pe)
		b := bufio.NewReader(os.Stdin)
		r, _, err := b.ReadLine()
		if err != nil {
			logger.Fatalf("unable to parse input: %s", err)
		}
		env[pe] = string(r)
	}
	for _, pa := range ext.Config.PromptArgs {
		fmt.Printf("enter value for container argument %s: ", pa)
		b := bufio.NewReader(os.Stdin)
		r, _, err := b.ReadLine()
		if err != nil {
			logger.Fatalf("unable to parse input: %s", err)
		}
		arg := string(r)
		if pa != "" {
			arg = fmt.Sprintf("%s=%s", pa, r)
		}
		args = append(args, arg)
	}
	ext.Config.Environment = env
	ext.Config.Args = args
	if err := m.AddExtension(ext); err != nil {
		logger.Fatalf("error adding extension: %s", err)
	}
	fmt.Printf("added extension name=%s version=%s\n", ext.Name, ext.Version)
}
Пример #12
0
func webhookKeyRemoveAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	removeKeys := c.Args()
	for _, key := range removeKeys {
		if err := m.RemoveWebhookKey(key); err != nil {
			logger.Fatalf("error removing webhook key: %s", err)
		}
		fmt.Printf("removed %s\n", key)
	}
}
Пример #13
0
func runAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	if c.String("name") == "" {
		logger.Fatal("you must specify an image name")
	}
	vols := c.StringSlice("vol")
	env := parseEnvironmentVariables(c.StringSlice("env"))
	ports := parsePorts(c.StringSlice("port"))
	links := parseContainerLinks(c.StringSlice("link"))
	policy, maxRetries, err := parseRestartPolicy(c.String("restart"))
	if err != nil {
		logger.Fatalf("error parsing restart policy: %s", err)
	}
	rp := citadel.RestartPolicy{
		Name:              policy,
		MaximumRetryCount: maxRetries,
	}
	image := &citadel.Image{
		Name:          c.String("name"),
		ContainerName: c.String("container-name"),
		Cpus:          c.Float64("cpus"),
		Cpuset:        c.String("cpuset"),
		Memory:        c.Float64("memory"),
		Hostname:      c.String("hostname"),
		Domainname:    c.String("domain"),
		NetworkMode:   c.String("network"),
		Labels:        c.StringSlice("label"),
		Args:          c.StringSlice("arg"),
		Environment:   env,
		Links:         links,
		Publish:       c.Bool("publish"),
		Volumes:       vols,
		BindPorts:     ports,
		RestartPolicy: rp,
		Type:          c.String("type"),
	}
	containers, err := m.Run(image, c.Int("count"), c.Bool("pull"))
	if err != nil {
		logger.Fatalf("error running container: %s\n", err)
	}
	for _, c := range containers {
		fmt.Printf("started %s on %s\n", c.ID[:12], c.Engine.ID)
	}
}
Пример #14
0
func removeExtensionAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	extIds := c.Args()
	if len(extIds) == 0 {
		return
	}
	for _, id := range extIds {
		if err := m.RemoveExtension(id); err != nil {
			logger.Fatalf("error removing extension: %s", err)
		}
	}
}
Пример #15
0
func engineInspectAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	if len(c.Args()) == 0 {
		logger.Fatal("you must specify an id")
	}
	id := c.Args()[0]
	eng, err := m.GetEngine(id)
	if err != nil {
		logger.Fatalf("error inspecting engine: %s", err)
	}
	b, err := json.MarshalIndent(eng, "", "    ")
	fmt.Println(string(b))
}
Пример #16
0
func serviceKeyRemoveAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	removeKeys := c.Args()
	for _, key := range removeKeys {
		k := &shipyard.ServiceKey{
			Key: key,
		}
		if err := m.RemoveServiceKey(k); err != nil {
			logger.Fatalf("error removing service key: %s", err)
		}
		fmt.Printf("removed %s\n", key)
	}
}
Пример #17
0
func containerInspectAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	args := c.Args()
	if len(args) == 0 {
		logger.Fatalf("you must specify a container id")
	}
	containerId := args[0]
	container, err := m.GetContainer(containerId)
	if err != nil {
		logger.Fatalf("error getting container info: %s", err)
	}
	b, err := json.MarshalIndent(container, "", "    ")
	fmt.Println(string(b))
}
Пример #18
0
func changePasswordAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	fmt.Printf("Password: "******"Confirm: ")
	p2 := gopass.GetPasswd()
	pass := strings.TrimSpace(string(p1[:]))
	pass_confirm := strings.TrimSpace(string(p2[:]))
	if pass != pass_confirm {
		logger.Fatal("passwords do not match")
	}
	if err := m.ChangePassword(pass); err != nil {
		logger.Fatal(err)
	}
}
Пример #19
0
func deleteAccountAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	accounts := c.Args()
	if len(accounts) == 0 {
		return
	}
	for _, acct := range accounts {
		account := &shipyard.Account{
			Username: acct,
		}
		if err := m.DeleteAccount(account); err != nil {
			logger.Fatalf("error deleting account: %s", err)
		}
	}
}
Пример #20
0
func scaleAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	containerId := c.String("id")
	count := c.Int("count")
	container, err := m.Container(containerId)
	if err != nil {
		logger.Fatalf("error getting container info: %s", err)
	}
	if containerId == "" {
		logger.Fatalf("you must specify a container id")
	}
	if err := m.Scale(container, count); err != nil {
		logger.Fatalf("error scaling container: %s\n", err)
	}
	fmt.Printf("scaled %s to %d\n", container.ID[:12], count)
}
Пример #21
0
func accountsAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	accounts, err := m.Accounts()
	if err != nil {
		logger.Fatalf("error getting accounts: %s", err)
	}
	if len(accounts) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "Username\tRole")
	for _, u := range accounts {
		fmt.Fprintf(w, "%s\t%s\n", u.Username, u.Role.Name)
	}
	w.Flush()
}
Пример #22
0
func extensionsAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	exts, err := m.Extensions()
	if err != nil {
		logger.Fatalf("error getting extensions: %s", err)
	}
	if len(exts) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "ID\tName\tVersion\tAuthor\tImage\tUrl\tDescription")
	for _, e := range exts {
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", e.ID, e.Name, e.Version, e.Author, e.Image, e.Url, e.Description)
	}
	w.Flush()
}
Пример #23
0
func webhookKeysListAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	keys, err := m.WebhookKeys()
	if err != nil {
		logger.Fatalf("error getting webhook keys: %s", err)
		return
	}
	if len(keys) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "Image\tKey")
	for _, k := range keys {
		fmt.Fprintf(w, "%s\t%s\n", k.Image, k.Key)
	}
	w.Flush()
}
Пример #24
0
func serviceKeysListAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	keys, err := m.ServiceKeys()
	if err != nil {
		logger.Fatalf("error getting service keys: %s", err)
		return
	}
	if len(keys) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "Key\tDescription")
	for _, k := range keys {
		fmt.Fprintf(w, "%s\t%s\n", k.Key, k.Description)
	}
	w.Flush()
}
Пример #25
0
func engineListAction(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	engines, err := m.Engines()
	if err != nil {
		logger.Fatalf("error getting engines: %s", err)
		return
	}
	if len(engines) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "ID\tCpus\tMemory\tHost\tLabels")
	for _, e := range engines {
		labels := strings.Join(e.Engine.Labels, ",")
		fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\t%s\n", e.Engine.ID, e.Engine.Cpus, e.Engine.Memory, e.Engine.Addr, labels)
	}
	w.Flush()
}
Пример #26
0
func engineRemoveAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	engines, err := m.Engines()
	if err != nil {
		logger.Fatalf("error removing engine: %s\n", err)
	}
	removeEngines := c.Args()
	for _, eng := range engines {
		// this can probably be more efficient
		for _, i := range removeEngines {
			if eng.ID == i {
				if err := m.RemoveEngine(eng); err != nil {
					logger.Fatalf("error removing engine: %s", err)
				}
				fmt.Printf("removed %s\n", eng.Engine.ID)
			}
		}
	}
}
Пример #27
0
func engineListAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	engines, err := m.Engines()
	if err != nil {
		logger.Fatalf("error getting engines: %s", err)
		return
	}
	if len(engines) == 0 {
		return
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
	fmt.Fprintln(w, "ID\tName\tCpus\tMemory\tHost\tLabels\tHealth\tResponse Time (ms)\tDocker Version")
	for _, e := range engines {
		labels := strings.Join(e.Engine.Labels, ",")
		responseTime := responseTimeToString(e.Health.ResponseTime)
		fmt.Fprintf(w, "%s\t%s\t%.2f\t%.2f\t%s\t%s\t%s\t%s\t%s\n", e.ID, e.Engine.ID, e.Engine.Cpus, e.Engine.Memory, e.Engine.Addr, labels, e.Health.Status, responseTime, e.DockerVersion)
	}
	w.Flush()
}
Пример #28
0
func addAccountAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	user := c.String("username")
	pass := c.String("password")
	if user == "" || pass == "" {
		logger.Fatalf("you must specify a username and password")
	}
	role, err := m.Role(c.String("role"))
	if err != nil {
		logger.Fatal(err)
	}
	account := &shipyard.Account{
		Username: user,
		Password: pass,
		Role:     role,
	}
	if err := m.AddAccount(account); err != nil {
		logger.Fatalf("error adding account: %s", err)
	}
}
Пример #29
0
func engineAddAction(c *cli.Context) {
	cfg, err := loadConfig(c)
	if err != nil {
		logger.Fatal(err)
	}
	m := client.NewManager(cfg)
	id := c.String("id")
	addr := c.String("addr")
	if id == "" || addr == "" {
		logger.Fatalf("you must specify an id and address")
	}
	engine := &citadel.Engine{
		ID:     id,
		Addr:   addr,
		Cpus:   c.Float64("cpus"),
		Memory: c.Float64("memory"),
		Labels: c.StringSlice("label"),
	}
	sslCertPath := c.String("ssl-cert")
	sslKeyPath := c.String("ssl-key")
	caCertPath := c.String("ca-cert")
	var (
		sslCertData = []byte{}
		sslKeyData  = []byte{}
		caCertData  = []byte{}
		sslErr      error
	)
	if sslCertPath != "" && sslKeyPath != "" && caCertPath != "" {
		sslCert, err := os.Open(sslCertPath)
		if err != nil {
			logger.Fatalf("unable to open ssl certificate: %s", err)
		}
		sslKey, err := os.Open(sslKeyPath)
		if err != nil {
			logger.Fatalf("unable to open ssl key: %s", err)
		}
		caCert, err := os.Open(caCertPath)
		if err != nil {
			logger.Fatalf("unable to open ca certificate: %s", err)
		}
		if _, err := sslCert.Stat(); err != nil {
			logger.Fatalf("ssl cert is not accessible: %s", err)
		}
		if _, err := sslKey.Stat(); err != nil {
			logger.Fatalf("ssl key is not accessible: %s", err)
		}
		if _, err := caCert.Stat(); err != nil {
			logger.Fatalf("ca cert is not accessible: %s", err)
		}
		sslCertData, sslErr = ioutil.ReadAll(sslCert)
		if sslErr != nil {
			logger.Fatalf("unable to read ssl certificate: %s", sslErr)
		}
		sslKeyData, sslErr = ioutil.ReadAll(sslKey)
		if sslErr != nil {
			logger.Fatalf("unable to read ssl key: %s", sslErr)
		}
		caCertData, sslErr = ioutil.ReadAll(caCert)
		if sslErr != nil {
			logger.Fatalf("unable to read ca certificate: %s", sslErr)
		}
	}
	shipyardEngine := &shipyard.Engine{
		SSLCertificate: string(sslCertData),
		SSLKey:         string(sslKeyData),
		CACertificate:  string(caCertData),
		Engine:         engine,
	}
	if err := m.AddEngine(shipyardEngine); err != nil {
		logger.Fatalf("error adding engine: %s", err)
	}
}