github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 RemoveCertByFingerprint(string) error 45 } 46 47 type lxdConfig interface { 48 ServerStatus() (*lxdshared.ServerState, error) 49 SetConfig(k, v string) error 50 } 51 52 type lxdInstances interface { 53 Instances(string, ...string) ([]lxdclient.Instance, error) 54 AddInstance(lxdclient.InstanceSpec) (*lxdclient.Instance, error) 55 RemoveInstances(string, ...string) error 56 Addresses(string) ([]network.Address, error) 57 } 58 59 type lxdProfiles interface { 60 CreateProfile(string, map[string]string) error 61 HasProfile(string) (bool, error) 62 } 63 64 type lxdImages interface { 65 EnsureImageExists(series string, sources []lxdclient.Remote, copyProgressHandler func(string)) error 66 } 67 68 func newRawProvider(spec environs.CloudSpec) (*rawProvider, error) { 69 client, err := newClient(spec, ioutil.ReadFile, utils.RunCommand) 70 if err != nil { 71 return nil, errors.Annotate(err, "creating LXD client") 72 } 73 74 raw := &rawProvider{ 75 lxdCerts: client, 76 lxdConfig: client, 77 lxdInstances: client, 78 lxdProfiles: client, 79 lxdImages: client, 80 Firewaller: common.NewFirewaller(), 81 } 82 return raw, nil 83 } 84 85 type readFileFunc func(string) ([]byte, error) 86 type runCommandFunc func(string, ...string) (string, error) 87 88 func newClient( 89 spec environs.CloudSpec, 90 readFile readFileFunc, 91 runCommand runCommandFunc, 92 ) (*lxdclient.Client, error) { 93 if spec.Endpoint != "" { 94 // We don't handle connecting to non-local lxd at present. 95 return nil, errors.NotValidf("endpoint %q", spec.Endpoint) 96 } 97 98 config, err := getRemoteConfig(readFile, runCommand) 99 if errors.IsNotFound(err) { 100 config = &lxdclient.Config{Remote: lxdclient.Local} 101 } else if err != nil { 102 return nil, errors.Trace(err) 103 } 104 105 client, err := lxdclient.Connect(*config, true) 106 if err != nil { 107 return nil, errors.Trace(err) 108 } 109 return client, nil 110 } 111 112 // getRemoteConfig returns a lxdclient.Config using a TCP-based remote 113 // if called from within an instance started by the LXD provider. Otherwise, 114 // it returns an errors satisfying errors.IsNotFound. 115 func getRemoteConfig(readFile readFileFunc, runCommand runCommandFunc) (*lxdclient.Config, error) { 116 readFileOrig := readFile 117 readFile = func(path string) ([]byte, error) { 118 data, err := readFileOrig(path) 119 if err != nil { 120 if os.IsNotExist(err) { 121 err = errors.NotFoundf("%s", path) 122 } 123 return nil, err 124 } 125 return data, nil 126 } 127 clientCert, err := readFile(clientCertPath) 128 if err != nil { 129 return nil, errors.Annotate(err, "reading client certificate") 130 } 131 clientKey, err := readFile(clientKeyPath) 132 if err != nil { 133 return nil, errors.Annotate(err, "reading client key") 134 } 135 serverCert, err := readFile(serverCertPath) 136 if err != nil { 137 return nil, errors.Annotate(err, "reading server certificate") 138 } 139 cert := lxdclient.NewCert(clientCert, clientKey) 140 hostAddress, err := getDefaultGateway(runCommand) 141 if err != nil { 142 return nil, errors.Annotate(err, "getting gateway address") 143 } 144 return &lxdclient.Config{ 145 lxdclient.Remote{ 146 Name: "remote", 147 Host: hostAddress, 148 Protocol: lxdclient.LXDProtocol, 149 Cert: &cert, 150 ServerPEMCert: string(serverCert), 151 }, 152 }, nil 153 } 154 155 func getDefaultGateway(runCommand runCommandFunc) (string, error) { 156 out, err := runCommand("ip", "route", "list", "match", "0/0") 157 if err != nil { 158 return "", errors.Trace(err) 159 } 160 if !strings.HasPrefix(string(out), "default via") { 161 return "", errors.Errorf(`unexpected output from "ip route": %s`, out) 162 } 163 fields := strings.Fields(string(out)) 164 return fields[2], nil 165 }