github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/lxd/environ_raw.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxd 7 8 import ( 9 "io/ioutil" 10 "os" 11 "path" 12 "strings" 13 14 "github.com/juju/errors" 15 "github.com/juju/utils" 16 "github.com/juju/utils/series" 17 lxdshared "github.com/lxc/lxd/shared" 18 19 "github.com/juju/juju/environs" 20 jujupaths "github.com/juju/juju/juju/paths" 21 "github.com/juju/juju/network" 22 "github.com/juju/juju/provider/common" 23 "github.com/juju/juju/tools/lxdclient" 24 ) 25 26 var ( 27 jujuConfDir = jujupaths.MustSucceed(jujupaths.ConfDir(series.LatestLts())) 28 clientCertPath = path.Join(jujuConfDir, "lxd-client.crt") 29 clientKeyPath = path.Join(jujuConfDir, "lxd-client.key") 30 serverCertPath = path.Join(jujuConfDir, "lxd-server.crt") 31 ) 32 33 type rawProvider struct { 34 lxdCerts 35 lxdConfig 36 lxdInstances 37 lxdProfiles 38 lxdImages 39 common.Firewaller 40 } 41 42 type lxdCerts interface { 43 AddCert(lxdclient.Cert) error 44 } 45 46 type lxdConfig interface { 47 ServerStatus() (*lxdshared.ServerState, error) 48 SetConfig(k, v string) error 49 } 50 51 type lxdInstances interface { 52 Instances(string, ...string) ([]lxdclient.Instance, error) 53 AddInstance(lxdclient.InstanceSpec) (*lxdclient.Instance, error) 54 RemoveInstances(string, ...string) error 55 Addresses(string) ([]network.Address, error) 56 } 57 58 type lxdProfiles interface { 59 CreateProfile(string, map[string]string) error 60 HasProfile(string) (bool, error) 61 } 62 63 type lxdImages interface { 64 EnsureImageExists(series string, sources []lxdclient.Remote, copyProgressHandler func(string)) error 65 } 66 67 func newRawProvider(spec environs.CloudSpec) (*rawProvider, error) { 68 client, err := newClient(spec, ioutil.ReadFile, utils.RunCommand) 69 if err != nil { 70 return nil, errors.Annotate(err, "creating LXD client") 71 } 72 73 raw := &rawProvider{ 74 lxdCerts: client, 75 lxdConfig: client, 76 lxdInstances: client, 77 lxdProfiles: client, 78 lxdImages: client, 79 Firewaller: common.NewFirewaller(), 80 } 81 return raw, nil 82 } 83 84 type readFileFunc func(string) ([]byte, error) 85 type runCommandFunc func(string, ...string) (string, error) 86 87 func newClient( 88 spec environs.CloudSpec, 89 readFile readFileFunc, 90 runCommand runCommandFunc, 91 ) (*lxdclient.Client, error) { 92 if spec.Endpoint != "" { 93 // We don't handle connecting to non-local lxd at present. 94 return nil, errors.NotValidf("endpoint %q", spec.Endpoint) 95 } 96 97 config, err := getRemoteConfig(readFile, runCommand) 98 if errors.IsNotFound(err) { 99 config = &lxdclient.Config{Remote: lxdclient.Local} 100 } else if err != nil { 101 return nil, errors.Trace(err) 102 } 103 104 client, err := lxdclient.Connect(*config) 105 if err != nil { 106 return nil, errors.Trace(err) 107 } 108 return client, nil 109 } 110 111 // getRemoteConfig returns a lxdclient.Config using a TCP-based remote 112 // if called from within an instance started by the LXD provider. Otherwise, 113 // it returns an errors satisfying errors.IsNotFound. 114 func getRemoteConfig(readFile readFileFunc, runCommand runCommandFunc) (*lxdclient.Config, error) { 115 readFileOrig := readFile 116 readFile = func(path string) ([]byte, error) { 117 data, err := readFileOrig(path) 118 if err != nil { 119 if os.IsNotExist(err) { 120 err = errors.NotFoundf("%s", path) 121 } 122 return nil, err 123 } 124 return data, nil 125 } 126 clientCert, err := readFile(clientCertPath) 127 if err != nil { 128 return nil, errors.Annotate(err, "reading client certificate") 129 } 130 clientKey, err := readFile(clientKeyPath) 131 if err != nil { 132 return nil, errors.Annotate(err, "reading client key") 133 } 134 serverCert, err := readFile(serverCertPath) 135 if err != nil { 136 return nil, errors.Annotate(err, "reading server certificate") 137 } 138 cert := lxdclient.NewCert(clientCert, clientKey) 139 hostAddress, err := getDefaultGateway(runCommand) 140 if err != nil { 141 return nil, errors.Annotate(err, "getting gateway address") 142 } 143 return &lxdclient.Config{ 144 lxdclient.Remote{ 145 Name: "remote", 146 Host: hostAddress, 147 Protocol: lxdclient.LXDProtocol, 148 Cert: &cert, 149 ServerPEMCert: string(serverCert), 150 }, 151 }, nil 152 } 153 154 func getDefaultGateway(runCommand runCommandFunc) (string, error) { 155 out, err := runCommand("ip", "route", "list", "match", "0/0") 156 if err != nil { 157 return "", errors.Trace(err) 158 } 159 if !strings.HasPrefix(string(out), "default via") { 160 return "", errors.Errorf(`unexpected output from "ip route": %s`, out) 161 } 162 fields := strings.Fields(string(out)) 163 return fields[2], nil 164 }