garm/vendor/github.com/lxc/lxd/client/connection.go
Gabriel Adrian Samfira e7f208367b
Error if we can't remove instance from provider
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2023-03-26 22:19:20 +03:00

358 lines
10 KiB
Go

package lxd
import (
"context"
"crypto/sha256"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery"
"github.com/gorilla/websocket"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/logger"
"github.com/lxc/lxd/shared/simplestreams"
)
// ConnectionArgs represents a set of common connection properties.
type ConnectionArgs struct {
// TLS certificate of the remote server. If not specified, the system CA is used.
TLSServerCert string
// TLS certificate to use for client authentication.
TLSClientCert string
// TLS key to use for client authentication.
TLSClientKey string
// TLS CA to validate against when in PKI mode.
TLSCA string
// User agent string
UserAgent string
// Authentication type
AuthType string
// Authentication interactor
AuthInteractor []httpbakery.Interactor
// Custom proxy
Proxy func(*http.Request) (*url.URL, error)
// Custom HTTP Client (used as base for the connection)
HTTPClient *http.Client
// TransportWrapper wraps the *http.Transport set by lxd
TransportWrapper func(*http.Transport) HTTPTransporter
// Controls whether a client verifies the server's certificate chain and host name.
InsecureSkipVerify bool
// Cookie jar
CookieJar http.CookieJar
// Skip automatic GetServer request upon connection
SkipGetServer bool
// Caching support for image servers
CachePath string
CacheExpiry time.Duration
}
// ConnectLXD lets you connect to a remote LXD daemon over HTTPs.
//
// A client certificate (TLSClientCert) and key (TLSClientKey) must be provided.
//
// If connecting to a LXD daemon running in PKI mode, the PKI CA (TLSCA) must also be provided.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
func ConnectLXD(url string, args *ConnectionArgs) (InstanceServer, error) {
return ConnectLXDWithContext(context.Background(), url, args)
}
// ConnectLXDWithContext lets you connect to a remote LXD daemon over HTTPs with context.Context.
//
// A client certificate (TLSClientCert) and key (TLSClientKey) must be provided.
//
// If connecting to a LXD daemon running in PKI mode, the PKI CA (TLSCA) must also be provided.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
func ConnectLXDWithContext(ctx context.Context, url string, args *ConnectionArgs) (InstanceServer, error) {
// Cleanup URL
url = strings.TrimSuffix(url, "/")
logger.Debug("Connecting to a remote LXD over HTTPS", logger.Ctx{"url": url})
return httpsLXD(ctx, url, args)
}
// ConnectLXDHTTP lets you connect to a VM agent over a VM socket.
func ConnectLXDHTTP(args *ConnectionArgs, client *http.Client) (InstanceServer, error) {
return ConnectLXDHTTPWithContext(context.Background(), args, client)
}
// ConnectLXDHTTPWithContext lets you connect to a VM agent over a VM socket with context.Context.
func ConnectLXDHTTPWithContext(ctx context.Context, args *ConnectionArgs, client *http.Client) (InstanceServer, error) {
logger.Debug("Connecting to a VM agent over a VM socket")
// Use empty args if not specified
if args == nil {
args = &ConnectionArgs{}
}
httpBaseURL, err := url.Parse("https://custom.socket")
if err != nil {
return nil, err
}
ctxConnected, ctxConnectedCancel := context.WithCancel(context.Background())
// Initialize the client struct
server := ProtocolLXD{
ctx: ctx,
httpBaseURL: *httpBaseURL,
httpProtocol: "custom",
httpUserAgent: args.UserAgent,
ctxConnected: ctxConnected,
ctxConnectedCancel: ctxConnectedCancel,
eventConns: make(map[string]*websocket.Conn),
eventListeners: make(map[string][]*EventListener),
}
// Setup the HTTP client
server.http = client
// Test the connection and seed the server information
if !args.SkipGetServer {
serverStatus, _, err := server.GetServer()
if err != nil {
return nil, err
}
// Record the server certificate
server.httpCertificate = serverStatus.Environment.Certificate
}
return &server, nil
}
// ConnectLXDUnix lets you connect to a remote LXD daemon over a local unix socket.
//
// If the path argument is empty, then $LXD_SOCKET will be used, if
// unset $LXD_DIR/unix.socket will be used and if that one isn't set
// either, then the path will default to /var/lib/lxd/unix.socket.
func ConnectLXDUnix(path string, args *ConnectionArgs) (InstanceServer, error) {
return ConnectLXDUnixWithContext(context.Background(), path, args)
}
// ConnectLXDUnixWithContext lets you connect to a remote LXD daemon over a local unix socket with context.Context.
//
// If the path argument is empty, then $LXD_SOCKET will be used, if
// unset $LXD_DIR/unix.socket will be used and if that one isn't set
// either, then the path will default to /var/lib/lxd/unix.socket.
func ConnectLXDUnixWithContext(ctx context.Context, path string, args *ConnectionArgs) (InstanceServer, error) {
logger.Debug("Connecting to a local LXD over a Unix socket")
// Use empty args if not specified
if args == nil {
args = &ConnectionArgs{}
}
httpBaseURL, err := url.Parse("http://unix.socket")
if err != nil {
return nil, err
}
ctxConnected, ctxConnectedCancel := context.WithCancel(context.Background())
// Initialize the client struct
server := ProtocolLXD{
ctx: ctx,
httpBaseURL: *httpBaseURL,
httpUnixPath: path,
httpProtocol: "unix",
httpUserAgent: args.UserAgent,
ctxConnected: ctxConnected,
ctxConnectedCancel: ctxConnectedCancel,
eventConns: make(map[string]*websocket.Conn),
eventListeners: make(map[string][]*EventListener),
}
// Determine the socket path
if path == "" {
path = os.Getenv("LXD_SOCKET")
if path == "" {
lxdDir := os.Getenv("LXD_DIR")
if lxdDir == "" {
lxdDir = "/var/lib/lxd"
}
path = filepath.Join(lxdDir, "unix.socket")
}
}
path = shared.HostPath(path)
// Setup the HTTP client
httpClient, err := unixHTTPClient(args, path)
if err != nil {
return nil, err
}
server.http = httpClient
// Test the connection and seed the server information
if !args.SkipGetServer {
serverStatus, _, err := server.GetServer()
if err != nil {
return nil, err
}
// Record the server certificate
server.httpCertificate = serverStatus.Environment.Certificate
}
return &server, nil
}
// ConnectPublicLXD lets you connect to a remote public LXD daemon over HTTPs.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
func ConnectPublicLXD(url string, args *ConnectionArgs) (ImageServer, error) {
return ConnectPublicLXDWithContext(context.Background(), url, args)
}
// ConnectPublicLXDWithContext lets you connect to a remote public LXD daemon over HTTPs with context.Context.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
func ConnectPublicLXDWithContext(ctx context.Context, url string, args *ConnectionArgs) (ImageServer, error) {
logger.Debug("Connecting to a remote public LXD over HTTPS")
// Cleanup URL
url = strings.TrimSuffix(url, "/")
return httpsLXD(ctx, url, args)
}
// ConnectSimpleStreams lets you connect to a remote SimpleStreams image server over HTTPs.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
func ConnectSimpleStreams(url string, args *ConnectionArgs) (ImageServer, error) {
logger.Debug("Connecting to a remote simplestreams server", logger.Ctx{"URL": url})
// Cleanup URL
url = strings.TrimSuffix(url, "/")
// Use empty args if not specified
if args == nil {
args = &ConnectionArgs{}
}
// Initialize the client struct
server := ProtocolSimpleStreams{
httpHost: url,
httpUserAgent: args.UserAgent,
httpCertificate: args.TLSServerCert,
}
// Setup the HTTP client
httpClient, err := tlsHTTPClient(args.HTTPClient, args.TLSClientCert, args.TLSClientKey, args.TLSCA, args.TLSServerCert, args.InsecureSkipVerify, args.Proxy, args.TransportWrapper)
if err != nil {
return nil, err
}
server.http = httpClient
// Get simplestreams client
ssClient := simplestreams.NewClient(url, *httpClient, args.UserAgent)
server.ssClient = ssClient
// Setup the cache
if args.CachePath != "" {
if !shared.PathExists(args.CachePath) {
return nil, fmt.Errorf("Cache directory %q doesn't exist", args.CachePath)
}
hashedURL := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
cachePath := filepath.Join(args.CachePath, hashedURL)
cacheExpiry := args.CacheExpiry
if cacheExpiry == 0 {
cacheExpiry = time.Hour
}
if !shared.PathExists(cachePath) {
err := os.Mkdir(cachePath, 0755)
if err != nil {
return nil, err
}
}
ssClient.SetCache(cachePath, cacheExpiry)
}
return &server, nil
}
// Internal function called by ConnectLXD and ConnectPublicLXD.
func httpsLXD(ctx context.Context, requestURL string, args *ConnectionArgs) (InstanceServer, error) {
// Use empty args if not specified
if args == nil {
args = &ConnectionArgs{}
}
httpBaseURL, err := url.Parse(requestURL)
if err != nil {
return nil, err
}
ctxConnected, ctxConnectedCancel := context.WithCancel(context.Background())
// Initialize the client struct
server := ProtocolLXD{
ctx: ctx,
httpCertificate: args.TLSServerCert,
httpBaseURL: *httpBaseURL,
httpProtocol: "https",
httpUserAgent: args.UserAgent,
bakeryInteractor: args.AuthInteractor,
ctxConnected: ctxConnected,
ctxConnectedCancel: ctxConnectedCancel,
eventConns: make(map[string]*websocket.Conn),
eventListeners: make(map[string][]*EventListener),
}
if args.AuthType == "candid" {
server.RequireAuthenticated(true)
}
// Setup the HTTP client
httpClient, err := tlsHTTPClient(args.HTTPClient, args.TLSClientCert, args.TLSClientKey, args.TLSCA, args.TLSServerCert, args.InsecureSkipVerify, args.Proxy, args.TransportWrapper)
if err != nil {
return nil, err
}
if args.CookieJar != nil {
httpClient.Jar = args.CookieJar
}
server.http = httpClient
if args.AuthType == "candid" {
server.setupBakeryClient()
}
// Test the connection and seed the server information
if !args.SkipGetServer {
_, _, err := server.GetServer()
if err != nil {
return nil, err
}
}
return &server, nil
}