terraform/vendor/github.com/ukcloud/govcloudair/api_vcd.go

246 lines
6.0 KiB
Go

package govcloudair
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"sync"
"time"
)
type VCDClient struct {
OrgHREF url.URL // vCloud Director OrgRef
Org Org // Org
OrgVdc Vdc // Org vDC
Client Client // Client for the underlying VCD instance
sessionHREF url.URL // HREF for the session API
QueryHREF url.URL // HREF for the query API
Mutex sync.Mutex
}
type supportedVersions struct {
VersionInfo struct {
Version string `xml:"Version"`
LoginUrl string `xml:"LoginUrl"`
} `xml:"VersionInfo"`
}
func (c *VCDClient) vcdloginurl() error {
s := c.Client.VCDVDCHREF
s.Path += "/versions"
// No point in checking for errors here
req := c.Client.NewRequest(map[string]string{}, "GET", s, nil)
resp, err := checkResp(c.Client.Http.Do(req))
if err != nil {
return err
}
defer resp.Body.Close()
supportedVersions := new(supportedVersions)
err = decodeBody(resp, supportedVersions)
if err != nil {
return fmt.Errorf("error decoding versions response: %s", err)
}
u, err := url.Parse(supportedVersions.VersionInfo.LoginUrl)
if err != nil {
return fmt.Errorf("couldn't find a LoginUrl in versions")
}
c.sessionHREF = *u
return nil
}
func (c *VCDClient) vcdauthorize(user, pass, org string) error {
if user == "" {
user = os.Getenv("VCLOUD_USERNAME")
}
if pass == "" {
pass = os.Getenv("VCLOUD_PASSWORD")
}
if org == "" {
org = os.Getenv("VCLOUD_ORG")
}
// No point in checking for errors here
req := c.Client.NewRequest(map[string]string{}, "POST", c.sessionHREF, nil)
// Set Basic Authentication Header
req.SetBasicAuth(user+"@"+org, pass)
// Add the Accept header for vCA
req.Header.Add("Accept", "application/*+xml;version=5.5")
resp, err := checkResp(c.Client.Http.Do(req))
if err != nil {
return err
}
defer resp.Body.Close()
// Store the authentication header
c.Client.VCDToken = resp.Header.Get("x-vcloud-authorization")
c.Client.VCDAuthHeader = "x-vcloud-authorization"
session := new(session)
err = decodeBody(resp, session)
if err != nil {
fmt.Errorf("error decoding session response: %s", err)
}
org_found := false
// Loop in the session struct to find the organization and query api.
for _, s := range session.Link {
if s.Type == "application/vnd.vmware.vcloud.org+xml" && s.Rel == "down" {
u, err := url.Parse(s.HREF)
if err != nil {
return fmt.Errorf("couldn't find a Organization in current session, %v", err)
}
c.OrgHREF = *u
org_found = true
}
if s.Type == "application/vnd.vmware.vcloud.query.queryList+xml" && s.Rel == "down" {
u, err := url.Parse(s.HREF)
if err != nil {
return fmt.Errorf("couldn't find a Query API in current session, %v", err)
}
c.QueryHREF = *u
}
}
if !org_found {
return fmt.Errorf("couldn't find a Organization in current session")
}
// Loop in the session struct to find the session url.
session_found := false
for _, s := range session.Link {
if s.Rel == "remove" {
u, err := url.Parse(s.HREF)
if err != nil {
return fmt.Errorf("couldn't find a logout HREF in current session, %v", err)
}
c.sessionHREF = *u
session_found = true
}
}
if !session_found {
return fmt.Errorf("couldn't find a logout HREF in current session")
}
return nil
}
func (c *VCDClient) RetrieveOrg(vcdname string) (Org, error) {
req := c.Client.NewRequest(map[string]string{}, "GET", c.OrgHREF, nil)
req.Header.Add("Accept", "vnd.vmware.vcloud.org+xml;version=5.5")
// TODO: wrap into checkresp to parse error
resp, err := checkResp(c.Client.Http.Do(req))
if err != nil {
return Org{}, fmt.Errorf("error retreiving org: %s", err)
}
org := NewOrg(&c.Client)
if err = decodeBody(resp, org.Org); err != nil {
return Org{}, fmt.Errorf("error decoding org response: %s", err)
}
// Get the VDC ref from the Org
for _, s := range org.Org.Link {
if s.Type == "application/vnd.vmware.vcloud.vdc+xml" && s.Rel == "down" {
if vcdname != "" && s.Name != vcdname {
continue
}
u, err := url.Parse(s.HREF)
if err != nil {
return Org{}, err
}
c.Client.VCDVDCHREF = *u
}
}
if &c.Client.VCDVDCHREF == nil {
return Org{}, fmt.Errorf("error finding the organization VDC HREF")
}
return *org, nil
}
func NewVCDClient(vcdEndpoint url.URL, insecure bool) *VCDClient {
return &VCDClient{
Client: Client{
APIVersion: "5.5",
VCDVDCHREF: vcdEndpoint,
Http: http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: 120 * time.Second,
},
},
},
}
}
// Authenticate is an helper function that performs a login in vCloud Director.
func (c *VCDClient) Authenticate(username, password, org, vdcname string) (Org, Vdc, error) {
// LoginUrl
err := c.vcdloginurl()
if err != nil {
return Org{}, Vdc{}, fmt.Errorf("error finding LoginUrl: %s", err)
}
// Authorize
err = c.vcdauthorize(username, password, org)
if err != nil {
return Org{}, Vdc{}, fmt.Errorf("error authorizing: %s", err)
}
// Get Org
o, err := c.RetrieveOrg(vdcname)
if err != nil {
return Org{}, Vdc{}, fmt.Errorf("error acquiring Org: %s", err)
}
vdc, err := c.Client.retrieveVDC()
if err != nil {
return Org{}, Vdc{}, fmt.Errorf("error retrieving the organization VDC")
}
return o, vdc, nil
}
// Disconnect performs a disconnection from the vCloud Director API endpoint.
func (c *VCDClient) Disconnect() error {
if c.Client.VCDToken == "" && c.Client.VCDAuthHeader == "" {
return fmt.Errorf("cannot disconnect, client is not authenticated")
}
req := c.Client.NewRequest(map[string]string{}, "DELETE", c.sessionHREF, nil)
// Add the Accept header for vCA
req.Header.Add("Accept", "application/xml;version=5.5")
// Set Authorization Header
req.Header.Add(c.Client.VCDAuthHeader, c.Client.VCDToken)
if _, err := checkResp(c.Client.Http.Do(req)); err != nil {
return fmt.Errorf("error processing session delete for vCloud Director: %s", err)
}
return nil
}