docs/backends/cloud.go

219 lines
6.6 KiB
Go

//
// Copyright (C) 2013 The Docker Cloud authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package backends
import (
"encoding/json"
"fmt"
"github.com/dotcloud/docker/engine"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
)
// The Cloud interface provides the contract that cloud providers should implement to enable
// running Docker containers in their cloud.
// TODO(bburns): Restructure this into Cloud, Instance and Tunnel interfaces
type Cloud interface {
// GetPublicIPAddress returns the stringified address (e.g "1.2.3.4") of the runtime
GetPublicIPAddress(name string, zone string) (string, error)
// CreateInstance creates a virtual machine instance given a name and a zone. Returns the
// IP address of the instance. Waits until Docker is up and functioning on the machine
// before returning.
CreateInstance(name string, zone string) (string, error)
// DeleteInstance deletes a virtual machine instance, given the instance name and zone.
DeleteInstance(name string, zone string) error
// Open a secure tunnel (generally SSH) between the local host and a remote host.
OpenSecureTunnel(name string, zone string, localPort int, remotePort int) (*os.Process, error)
}
func CloudBackend() engine.Installer {
return &cloud{}
}
type cloud struct {
}
type Container struct {
Id string
Image string
Tty bool
}
// returns true, if the connection was successful, false otherwise
type Tunnel struct {
url.URL
}
func (t Tunnel) isActive() bool {
_, err := http.Get(t.String())
return err == nil
}
func (s *cloud) Install(eng *engine.Engine) error {
eng.Register("cloud", func(job *engine.Job) engine.Status {
if len(job.Args) < 3 {
return job.Errorf("usage: %s <provider> <instance> <zone> <aditional> <provider> <args> <proto>://<addr>", job.Name)
}
instance := job.Args[1]
zone := job.Args[2]
var cloud Cloud
var err error
switch job.Args[0] {
case "gce":
if len(job.Args) < 4 {
return job.Errorf("usage: %s gce <instance> <zone> <project>")
}
cloud, err = NewCloudGCE(job.Args[3])
if err != nil {
return job.Errorf("Unexpected error: %#v", err)
}
default:
return job.Errorf("Unknown cloud provider: %s", job.Args[0])
}
ip, err := cloud.GetPublicIPAddress(instance, zone)
instanceRunning := len(ip) > 0
if !instanceRunning {
log.Print("Instance doesn't exist, creating....")
_, err = cloud.CreateInstance(instance, zone)
}
if err != nil {
return job.Errorf("Unexpected error: %#v", err)
}
remotePort := 8000
localPort := 8001
apiVersion := "v1.10"
tunnelUrl, err := url.Parse(fmt.Sprintf("http://localhost:%d/%s/containers/json", localPort, apiVersion))
if err != nil {
return job.Errorf("Unexpected error: %#v", err)
}
tunnel := Tunnel{*tunnelUrl}
if !tunnel.isActive() {
fmt.Printf("Creating tunnel")
_, err = cloud.OpenSecureTunnel(instance, zone, localPort, remotePort)
if err != nil {
return job.Errorf("Failed to open tunnel: %#v", err)
}
}
host := fmt.Sprintf("tcp://localhost:%d", localPort)
client := newClient()
client.setURL(host)
client.version = apiVersion
//job.Eng.Register("inspect", func(job *engine.Job) engine.Status {
// resp, err := client.call("GET", "/containers/
job.Eng.Register("create", func(job *engine.Job) engine.Status {
container := Container{
Image: job.Getenv("Image"),
Tty: job.Getenv("Tty") == "true",
}
data, err := json.Marshal(container)
resp, err := client.call("POST", "/containers/create", string(data))
if err != nil {
return job.Errorf("post: %v", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return job.Errorf("read body: %#v", err)
}
var containerOut Container
err = json.Unmarshal([]byte(body), &containerOut)
_, err = job.Printf("%s\n", containerOut.Id)
if err != nil {
return job.Errorf("write body: %#v", err)
}
log.Printf("%s", string(body))
return engine.StatusOK
})
job.Eng.Register("start", func(job *engine.Job) engine.Status {
path := fmt.Sprintf("/containers/%s/start", job.Args[0])
resp, err := client.call("POST", path, "{\"Binds\":[],\"ContainerIDFile\":\"\",\"LxcConf\":[],\"Privileged\":false,\"PortBindings\":{},\"Links\":null,\"PublishAllPorts\":false,\"Dns\":null,\"DnsSearch\":[],\"VolumesFrom\":[]}")
if err != nil {
return job.Errorf("post: %v", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return job.Errorf("read body: %#v", err)
}
log.Printf("%s", string(body))
return engine.StatusOK
})
job.Eng.Register("containers", func(job *engine.Job) engine.Status {
path := fmt.Sprintf(
"/containers/json?all=%s&size=%s&since=%s&before=%s&limit=%s",
url.QueryEscape(job.Getenv("all")),
url.QueryEscape(job.Getenv("size")),
url.QueryEscape(job.Getenv("since")),
url.QueryEscape(job.Getenv("before")),
url.QueryEscape(job.Getenv("limit")),
)
resp, err := client.call("GET", path, "")
if err != nil {
return job.Errorf("get: %v", err)
}
// FIXME: check for response error
c := engine.NewTable("Created", 0)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return job.Errorf("read body: %v", err)
}
fmt.Printf("---> '%s'\n", body)
if _, err := c.ReadListFrom(body); err != nil {
return job.Errorf("readlist: %v", err)
}
c.WriteListTo(job.Stdout)
return engine.StatusOK
})
job.Eng.Register("container_delete", func(job *engine.Job) engine.Status {
log.Printf("%#v", job.Args)
path := "/containers/" + job.Args[0]
resp, err := client.call("DELETE", path, "")
if err != nil {
return job.Errorf("delete: %v", err)
}
log.Printf("%#v", resp)
return engine.StatusOK
})
job.Eng.Register("stop", func(job *engine.Job) engine.Status {
log.Printf("%#v", job.Args)
path := "/containers/" + job.Args[0] + "/stop"
resp, err := client.call("POST", path, "")
if err != nil {
return job.Errorf("delete: %v", err)
}
log.Printf("%#v", resp)
return engine.StatusOK
})
job.Eng.RegisterCatchall(func(job *engine.Job) engine.Status {
log.Printf("%#v %#v %#v", *job, job.Env(), job.Args)
return engine.StatusOK
})
return engine.StatusOK
})
return nil
}