/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ /* This package provides an interface to fake a CSP API for QEMU instances. */ package qemu import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "github.com/edgelesssys/constellation/v2/internal/cloud/metadata" "github.com/edgelesssys/constellation/v2/internal/constants" ) const qemuMetadataEndpoint = "10.42.0.1:8080" // Cloud provides an interface to fake a CSP API for QEMU instances. type Cloud struct{} // New returns a new Cloud instance. func New() *Cloud { return &Cloud{} } // List retrieves all instances belonging to the current constellation. func (c *Cloud) List(ctx context.Context) ([]metadata.InstanceMetadata, error) { instancesRaw, err := c.retrieveMetadata(ctx, "/peers") if err != nil { return nil, err } var instances []metadata.InstanceMetadata err = json.Unmarshal(instancesRaw, &instances) return instances, err } // Self retrieves the current instance. func (c *Cloud) Self(ctx context.Context) (metadata.InstanceMetadata, error) { instanceRaw, err := c.retrieveMetadata(ctx, "/self") if err != nil { return metadata.InstanceMetadata{}, err } var instance metadata.InstanceMetadata err = json.Unmarshal(instanceRaw, &instance) return instance, err } // GetLoadBalancerEndpoint returns the endpoint of the load balancer. // For QEMU, the load balancer is the first control plane node returned by the metadata API. func (c *Cloud) GetLoadBalancerEndpoint(ctx context.Context) (host, port string, err error) { host, err = c.getLoadBalancerHost(ctx) if err != nil { return "", "", fmt.Errorf("getting load balancer host: %w", err) } return host, strconv.FormatInt(constants.KubernetesPort, 10), nil } func (c *Cloud) getLoadBalancerHost(ctx context.Context) (string, error) { endpointRaw, err := c.retrieveMetadata(ctx, "/endpoint") if err != nil { return "", err } var endpoint string err = json.Unmarshal(endpointRaw, &endpoint) return endpoint, err } // InitSecretHash returns the hash of the init secret. func (c *Cloud) InitSecretHash(ctx context.Context) ([]byte, error) { initSecretHash, err := c.retrieveMetadata(ctx, "/initsecrethash") if err != nil { return nil, fmt.Errorf("could not retrieve init secret hash: %w", err) } return initSecretHash, nil } // UID returns the UID of the constellation. func (c *Cloud) UID(_ context.Context) (string, error) { // We expect only one constellation to be deployed in the same QEMU / libvirt environment. // the UID can be an empty string. return "", nil } func (c *Cloud) retrieveMetadata(ctx context.Context, uri string) ([]byte, error) { url := &url.URL{ Scheme: "http", Host: qemuMetadataEndpoint, Path: uri, } req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil) if err != nil { return nil, err } res, err := (&http.Client{}).Do(req) if err != nil { return nil, err } defer res.Body.Close() return io.ReadAll(res.Body) }