From 85712d6258f4ba511a5e823317c4275c4554ae1f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:42:28 +0000 Subject: [PATCH 01/11] Revert "api: Add `ubuntu_pro_guest_attach` API extension." This reverts commit 5ecafb1b81602cfecf09cef0fe4c860a759f2ee8. Signed-off-by: Thomas Parrott --- doc/api-extensions.md | 5 ----- shared/version/api.go | 1 - 2 files changed, 6 deletions(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 9ec7dcfe6a63..d9a4d00d2978 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2471,11 +2471,6 @@ This introduces per-pool project disk limits, introducing a `limits.disk.pool.NA configuration option to the project limits. When `limits.disk.pool.POOLNAME: 0` for a project, the pool is excluded from `lxc storage list` in that project. -## `ubuntu_pro_guest_attach` - -Adds a new {config:option}`instance-miscellaneous:ubuntu_pro.guest_attach` configuration option for instances. -When set to `on`, if the host has guest attachment enabled, the guest can request a guest token for Ubuntu Pro via `devlxd`. - ## `metadata_configuration_entity_types` This adds entity type metadata to `GET /1.0/metadata/configuration`. diff --git a/shared/version/api.go b/shared/version/api.go index 84a109007f67..c0f3ad85a703 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -416,7 +416,6 @@ var APIExtensions = []string{ "disk_io_bus_virtio_blk", "metrics_api_requests", "projects_limits_disk_pool", - "ubuntu_pro_guest_attach", "metadata_configuration_entity_types", "access_management_tls", "network_allocations_ovn_uplink", From dd6a47e2e8f0c2d5a73605b04dd967332be86cbb Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:42:42 +0000 Subject: [PATCH 02/11] Revert "shared/api: Add guest attachment API responses." This reverts commit 014571941fd9d8d7fe042469195fa575f8542416. Signed-off-by: Thomas Parrott --- shared/api/devlxd.go | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/shared/api/devlxd.go b/shared/api/devlxd.go index dcf9c385cf29..340d38bdd717 100644 --- a/shared/api/devlxd.go +++ b/shared/api/devlxd.go @@ -23,34 +23,3 @@ type DevLXDGet struct { // Example: lxd01 Location string `json:"location" yaml:"location"` } - -// UbuntuProGuestTokenResponse contains the expected fields of proAPIGetGuestTokenV1 that must be passed back to -// the guest for pro attachment to succeed. -// -// API extension: ubuntu_pro_guest_attach. -type UbuntuProGuestTokenResponse struct { - // Expires denotes the time at which the token will expire. - // - // Example: 2025-03-23T20:00:00-04:00 - Expires string `json:"expires"` - - // GuestToken contains the guest Pro attach token. - // - // Example: RANDOM-STRING - GuestToken string `json:"guest_token"` - - // ID is an identifier for the token. - // - // Example: 9f65c3d0-c326-491e-927f-9b062b6649a0 - ID string `json:"id"` -} - -// UbuntuProSettings contains Ubuntu Pro settings relevant to LXD. -// -// API extension: ubuntu_pro_guest_attach. -type UbuntuProSettings struct { - // GuestAttach indicates the availability of ubuntu pro guest attachment. - // - // Example: on - GuestAttach string `json:"guest_attach"` -} From 505db29526c4a46b4af5950f1870ca38ab793283 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:43:17 +0000 Subject: [PATCH 03/11] Revert "doc: Document devlxd Ubuntu Pro API endpoints." This reverts commit f3306f1bbe2f4b26ab37f93084d2ff438218ca8d. Signed-off-by: Thomas Parrott --- doc/dev-lxd.md | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/doc/dev-lxd.md b/doc/dev-lxd.md index 1e29d475000e..e09063427e60 100644 --- a/doc/dev-lxd.md +++ b/doc/dev-lxd.md @@ -232,35 +232,3 @@ Return value: #cloud-config instance-id: af6a01c7-f847-4688-a2a4-37fddd744625 local-hostname: abc - -#### `/1.0/ubuntu-pro` - -##### GET - -* Description: Get Ubuntu Pro guest attachment setting for the instance -* Return: JSON object - -Return value - -```json -{ - "guest_attach": "on" -} -``` - -#### `/1.0/ubuntu-pro/token` - -##### POST - -* Description: Get an Ubuntu Pro guest attachment token -* Return: JSON object - -Return value - -```json -{ - "expires": "2025-03-23T20:00:00-04:00", - "token": "", - "id": "9f65c3d0-c326-491e-927f-9b062b6649a0" -} -``` From 7121a314136d033c6de0b7e8f7a6b46396ffb8a1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:44:54 +0000 Subject: [PATCH 04/11] Revert "lxd/ubuntupro: Return host guest attachment setting if instance setting is unset." This reverts commit c7f398d93ae1655ade42148421b3e5b3260dff9d. Signed-off-by: Thomas Parrott --- lxd/ubuntupro/client.go | 4 ++-- lxd/ubuntupro/client_test.go | 37 +++++++----------------------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/lxd/ubuntupro/client.go b/lxd/ubuntupro/client.go index 8ff5d1c7d057..107866d0c86f 100644 --- a/lxd/ubuntupro/client.go +++ b/lxd/ubuntupro/client.go @@ -125,9 +125,9 @@ func (s *Client) getGuestAttachSetting(instanceSetting string) string { return guestAttachSettingOff } - // The `ubuntu_pro.guest_attach` setting is optional. If it is not set, return the host's guest attach setting. + // The `ubuntu_pro.guest_attach` setting is optional. if instanceSetting == "" { - return s.guestAttachSetting + return guestAttachSettingOff } // If the setting is not empty, check it is valid. This should have been validated already when setting the value so diff --git a/lxd/ubuntupro/client_test.go b/lxd/ubuntupro/client_test.go index 45e866df95dc..c7bdcfef7ddd 100644 --- a/lxd/ubuntupro/client_test.go +++ b/lxd/ubuntupro/client_test.go @@ -91,36 +91,13 @@ func TestClient(t *testing.T) { }, } - assertionsWhenHostHasGuestAttachmentAvailable := []assertion{ + assertionsWhenHostHasGuestAttachmentOnOrAvailable := []assertion{ { - instanceSetting: "", - expectedSetting: guestAttachSettingAvailable, - expectedToken: &mockTokenResponse, - }, - { - instanceSetting: guestAttachSettingOff, + instanceSetting: "", expectedSetting: guestAttachSettingOff, expectErr: true, expectedErrorCode: http.StatusForbidden, }, - { - instanceSetting: guestAttachSettingAvailable, - expectedSetting: guestAttachSettingAvailable, - expectedToken: &mockTokenResponse, - }, - { - instanceSetting: guestAttachSettingOn, - expectedSetting: guestAttachSettingOn, - expectedToken: &mockTokenResponse, - }, - } - - assertionsWhenHostHasGuestAttachmentOn := []assertion{ - { - instanceSetting: "", - expectedSetting: guestAttachSettingOn, - expectedToken: &mockTokenResponse, - }, { instanceSetting: guestAttachSettingOff, expectedSetting: guestAttachSettingOff, @@ -192,7 +169,7 @@ func TestClient(t *testing.T) { // Write '{"guest_attach":"available"}' to the settings file. writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingAvailable) assert.Equal(t, guestAttachSettingAvailable, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentAvailable) + runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) // Write '{"guest_attach":"off"}' to the settings file. writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOff) @@ -202,7 +179,7 @@ func TestClient(t *testing.T) { // Write '{"guest_attach":"on"}' to the settings file. writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOn) + runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) // Write invalid JSON to the settings file. writeSettingsFile(lxdConfigFilepath, "{{}\\foo", "") @@ -212,7 +189,7 @@ func TestClient(t *testing.T) { // Write '{"guest_attach":"on"}' to the settings file. writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOn) + runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) // Write an invalid setting to the settings file. writeSettingsFile(lxdConfigFilepath, "", "foo") @@ -222,7 +199,7 @@ func TestClient(t *testing.T) { // Write '{"guest_attach":"on"}' to the settings file. writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOn) + runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) // Remove the config file. err = os.Remove(lxdConfigFilepath) @@ -243,7 +220,7 @@ func TestClient(t *testing.T) { require.NoError(t, err) sleep() assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOn) + runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) // Cancel the context. cancel() From ca995e015f19d2aeebbec763bb71616fbd78e933 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:45:19 +0000 Subject: [PATCH 05/11] Revert "lxd/ubuntupro: Add ubuntupro package." This reverts commit a776a06141501eedd9b6324e784396233701081f. Signed-off-by: Thomas Parrott --- lxd/ubuntupro/client.go | 247 ----------------------------------- lxd/ubuntupro/client_test.go | 233 --------------------------------- 2 files changed, 480 deletions(-) delete mode 100644 lxd/ubuntupro/client.go delete mode 100644 lxd/ubuntupro/client_test.go diff --git a/lxd/ubuntupro/client.go b/lxd/ubuntupro/client.go deleted file mode 100644 index 107866d0c86f..000000000000 --- a/lxd/ubuntupro/client.go +++ /dev/null @@ -1,247 +0,0 @@ -package ubuntupro - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/fs" - "net/http" - "os" - "path" - - "github.com/canonical/lxd/lxd/fsmonitor" - "github.com/canonical/lxd/lxd/fsmonitor/drivers" - "github.com/canonical/lxd/shared" - "github.com/canonical/lxd/shared/api" - "github.com/canonical/lxd/shared/logger" -) - -const ( - // guestAttachSettingOff indicates that guest attachment is turned off. - // - When the host has this setting turned off, devlxd requests to `GET /1.0/ubuntu-pro` should return "off" and - // `POST /1.0/ubuntu-pro/token` should return a 403 Forbidden (regardless of the guest setting). - // - When the guest has this setting turned off (`ubuntu_pro.guest_attach`), devlxd requests to `GET /1.0/ubuntu-pro` - // should return "off" and `POST /1.0/ubuntu-pro/token` should return a 403 Forbidden (regardless of the host setting). - guestAttachSettingOff = "off" - - // guestAttachSettingAvailable indicates that guest attachment is available. - // - When the host has this setting, devlxd requests to `GET /1.0/ubuntu-pro` should return the setting from the guest - // (`ubuntu_pro.guest_attach) and `POST /1.0/ubuntu-pro/token` should retrieve a guest token via the Ubuntu Pro client. - // - When the guest has this setting, the pro client inside the guest will not try to retrieve a guest token at startup - // (though attachment with a guest token can still be performed with `pro auto-attach`. - guestAttachSettingAvailable = "available" - - // guestAttachSettingOn indicates that guest attachment is on. - // - When the host has this setting, devlxd requests to `GET /1.0/ubuntu-pro` should return the setting from the guest - // (`ubuntu_pro.guest_attach) and `POST /1.0/ubuntu-pro/token` should retrieve a guest token via the Ubuntu Pro client. - // - When the guest has this setting, the pro client inside the guest will attempt to retrieve a guest token at startup. - guestAttachSettingOn = "on" -) - -// isValid returns an error if the GuestAttachSetting is not one of the pre-defined values. -func validateGuestAttachSetting(guestAttachSetting string) error { - if !shared.ValueInSlice(guestAttachSetting, []string{guestAttachSettingOff, guestAttachSettingAvailable, guestAttachSettingOn}) { - return fmt.Errorf("Invalid guest auto-attach setting %q", guestAttachSetting) - } - - return nil -} - -// ubuntuAdvantageDirectory is the base directory for Ubuntu Pro related configuration. -const ubuntuAdvantageDirectory = "/var/lib/ubuntu-advantage" - -// Client is our wrapper for Ubuntu Pro configuration and the Ubuntu Pro CLI. -type Client struct { - guestAttachSetting string - monitor fsmonitor.FSMonitor - pro pro -} - -// pro is an internal interface that is used for mocking calls to the pro CLI. -type pro interface { - getGuestToken(ctx context.Context) (*api.UbuntuProGuestTokenResponse, error) -} - -// proCLI calls the actual Ubuntu Pro CLI to implement the interface. -type proCLI struct{} - -// proAPIGetGuestTokenV1 represents the expected format of calls to `pro api u.pro.attach.guest.get_guest_token.v1`. -// Not all fields are modelled as they are not required for guest attachment functionality. -type proAPIGetGuestTokenV1 struct { - Result string `json:"result"` - Data struct { - Attributes api.UbuntuProGuestTokenResponse `json:"attributes"` - } `json:"data"` - Errors []struct { - Title string - } `json:"errors"` -} - -// getTokenJSON runs `pro api u.pro.attach.guest.get_guest_token.v1` and returns the result. -func (proCLI) getGuestToken(ctx context.Context) (*api.UbuntuProGuestTokenResponse, error) { - // Run pro guest attach command. - response, err := shared.RunCommandContext(ctx, "pro", "api", "u.pro.attach.guest.get_guest_token.v1") - if err != nil { - return nil, api.StatusErrorf(http.StatusServiceUnavailable, "Ubuntu Pro client command unsuccessful: %w", err) - } - - var getGuestTokenResponse proAPIGetGuestTokenV1 - err = json.Unmarshal([]byte(response), &getGuestTokenResponse) - if err != nil { - return nil, api.StatusErrorf(http.StatusInternalServerError, "Received unexpected response from Ubuntu Pro contracts server: %w", err) - } - - if getGuestTokenResponse.Result != "success" { - if len(getGuestTokenResponse.Errors) > 0 && getGuestTokenResponse.Errors[0].Title != "" { - return nil, api.StatusErrorf(http.StatusServiceUnavailable, "Ubuntu Pro contracts server returned %q when getting a guest token with error %q", getGuestTokenResponse.Result, getGuestTokenResponse.Errors[0].Title) - } - - return nil, api.StatusErrorf(http.StatusServiceUnavailable, "Ubuntu Pro contracts server returned %q when getting a guest token", getGuestTokenResponse.Result) - } - - return &getGuestTokenResponse.Data.Attributes, nil -} - -// New returns a new Client that watches /var/lib/ubuntu-advantage for changes to LXD configuration and contains a shim -// for the actual Ubuntu Pro CLI. If the host is not Ubuntu, it returns a static Client that always returns -// guestAttachSettingOff. -func New(osName string, ctx context.Context) *Client { - if osName != "Ubuntu" { - // If we're not on Ubuntu, return a static Client. - return &Client{guestAttachSetting: guestAttachSettingOff} - } - - s := &Client{} - s.init(ctx, shared.HostPath(ubuntuAdvantageDirectory), proCLI{}) - return s -} - -// getGuestAttachSetting returns the correct attachment setting for an instance based the on the instance configuration -// and the current GuestAttachSetting of the host. -func (s *Client) getGuestAttachSetting(instanceSetting string) string { - // If the setting is "off" on the host then no guest attachment should take place. - if s.guestAttachSetting == guestAttachSettingOff { - return guestAttachSettingOff - } - - // The `ubuntu_pro.guest_attach` setting is optional. - if instanceSetting == "" { - return guestAttachSettingOff - } - - // If the setting is not empty, check it is valid. This should have been validated already when setting the value so - // log a warning if it is invalid. - err := validateGuestAttachSetting(instanceSetting) - if err != nil { - logger.Warn("Received invalid Ubuntu Pro guest attachment setting", logger.Ctx{"setting": instanceSetting}) - return guestAttachSettingOff - } - - return instanceSetting -} - -// GuestAttachSettings returns UbuntuProSettings based on the instance configuration and the GuestAttachSetting of the host. -func (s *Client) GuestAttachSettings(instanceSetting string) api.UbuntuProSettings { - return api.UbuntuProSettings{GuestAttach: s.getGuestAttachSetting(instanceSetting)} -} - -// GetGuestToken returns a 403 Forbidden error if the host or the instance has guestAttachSettingOff, otherwise -// it calls the pro shim to get a token. -func (s *Client) GetGuestToken(ctx context.Context, instanceSetting string) (*api.UbuntuProGuestTokenResponse, error) { - if s.getGuestAttachSetting(instanceSetting) == guestAttachSettingOff { - return nil, api.NewStatusError(http.StatusForbidden, "Guest attachment not allowed") - } - - return s.pro.getGuestToken(ctx) -} - -// init configures the Client to watch the ubuntu advantage directory for file changes. -func (s *Client) init(ctx context.Context, ubuntuAdvantageDir string, proShim pro) { - // Initial setting should be "off". - s.guestAttachSetting = guestAttachSettingOff - s.pro = proShim - - // Set up a watcher on the ubuntu advantage directory. - err := s.watch(ctx, ubuntuAdvantageDir) - if err != nil { - logger.Warn("Failed to configure Ubuntu configuration watcher", logger.Ctx{"err": err}) - } -} - -func (s *Client) watch(ctx context.Context, ubuntuAdvantageDir string) error { - // On first call, attempt to read the contents of the config file. - configFilePath := path.Join(ubuntuAdvantageDir, "interfaces", "lxd-config.json") - err := s.parseConfigFile(configFilePath) - if err != nil && !errors.Is(err, fs.ErrNotExist) { - logger.Warn("Failed to read Ubunto Pro LXD configuration file", logger.Ctx{"err": err}) - } - - // Watch /var/lib/ubuntu-advantage for write, remove, and rename events. - monitor, err := drivers.Load(ctx, ubuntuAdvantageDir, fsmonitor.EventWrite, fsmonitor.EventRemove, fsmonitor.EventRename) - if err != nil { - return fmt.Errorf("Failed to create a file monitor: %w", err) - } - - go func() { - // Wait for the context to be cancelled. - <-ctx.Done() - - // On cancel, set the guestAttachSetting back to "off" and unwatch the file. - s.guestAttachSetting = guestAttachSettingOff - err := monitor.Unwatch(path.Join(ubuntuAdvantageDir, "interfaces", "lxd-config.json"), "") - if err != nil { - logger.Warn("Failed to remove Ubuntu Pro configuration file watcher", logger.Ctx{"err": err}) - } - }() - - // Add a hook for the config file. - err = monitor.Watch(configFilePath, "", func(path string, event fsmonitor.Event) bool { - if event == fsmonitor.EventRemove { - // On remove, set guest attach to "off". - s.guestAttachSetting = guestAttachSettingOff - return true - } - - // Otherwise, parse the config file and update the client accordingly. - err := s.parseConfigFile(path) - if err != nil { - logger.Warn("Failed to read Ubunto Pro LXD configuration file", logger.Ctx{"err": err}) - } - - return true - }) - if err != nil { - return fmt.Errorf("Failed to configure file monitor: %w", err) - } - - s.monitor = monitor - return nil -} - -// parseConfigFile reads the Ubuntu Pro `lxd-config.json` file, validates it, and sets appropriate values in the Client. -func (s *Client) parseConfigFile(lxdConfigFile string) error { - // Default to "off" if any error occurs. - s.guestAttachSetting = guestAttachSettingOff - - f, err := os.Open(lxdConfigFile) - if err != nil { - return fmt.Errorf("Failed to open Ubuntu Pro configuration file: %w", err) - } - - defer f.Close() - - var settings api.UbuntuProSettings - err = json.NewDecoder(f).Decode(&settings) - if err != nil { - return fmt.Errorf("Failed to read Ubuntu Pro configuration file: %w", err) - } - - err = validateGuestAttachSetting(settings.GuestAttach) - if err != nil { - return fmt.Errorf("Failed to read Ubuntu Pro configuration file: %w", err) - } - - s.guestAttachSetting = settings.GuestAttach - return nil -} diff --git a/lxd/ubuntupro/client_test.go b/lxd/ubuntupro/client_test.go deleted file mode 100644 index c7bdcfef7ddd..000000000000 --- a/lxd/ubuntupro/client_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package ubuntupro - -import ( - "context" - "encoding/json" - "net/http" - "os" - "path/filepath" - "testing" - "time" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/canonical/lxd/shared/api" -) - -type proCLIMock struct { - mockResponse *api.UbuntuProGuestTokenResponse - mockErr error -} - -func (p proCLIMock) getGuestToken(_ context.Context) (*api.UbuntuProGuestTokenResponse, error) { - return p.mockResponse, p.mockErr -} - -func TestClient(t *testing.T) { - sleep := func() { - time.Sleep(100 * time.Millisecond) - } - - writeSettingsFile := func(filepath string, raw string, setting string) { - var d []byte - var err error - if raw != "" { - d = []byte(raw) - } else { - d, err = json.Marshal(api.UbuntuProSettings{GuestAttach: setting}) - require.NoError(t, err) - } - - err = os.WriteFile(filepath, d, 0666) - require.NoError(t, err) - sleep() - } - - mockTokenResponse := api.UbuntuProGuestTokenResponse{ - Expires: time.Now().String(), - GuestToken: "token", - ID: uuid.New().String(), - } - - mockProCLI := proCLIMock{ - mockResponse: &mockTokenResponse, - mockErr: nil, - } - - type assertion struct { - instanceSetting string - expectedSetting string - expectErr bool - expectedToken *api.UbuntuProGuestTokenResponse - expectedErrorCode int - } - - assertionsWhenHostHasGuestAttachmentOff := []assertion{ - { - instanceSetting: "", - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - { - instanceSetting: guestAttachSettingOff, - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - { - instanceSetting: guestAttachSettingAvailable, - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - { - instanceSetting: guestAttachSettingOn, - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - } - - assertionsWhenHostHasGuestAttachmentOnOrAvailable := []assertion{ - { - instanceSetting: "", - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - { - instanceSetting: guestAttachSettingOff, - expectedSetting: guestAttachSettingOff, - expectErr: true, - expectedErrorCode: http.StatusForbidden, - }, - { - instanceSetting: guestAttachSettingAvailable, - expectedSetting: guestAttachSettingAvailable, - expectedToken: &mockTokenResponse, - }, - { - instanceSetting: guestAttachSettingOn, - expectedSetting: guestAttachSettingOn, - expectedToken: &mockTokenResponse, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - - // Make a temporary directory to test file watcher behaviour. - tmpDir, err := os.MkdirTemp("", "") - require.NoError(t, err) - - // Create and initialise the Client. Don't call New(), as this will create a real client watching the actual - // /var/lib/ubuntu-advantage directory. - s := &Client{} - s.init(ctx, tmpDir, mockProCLI) - - runAssertions := func(assertions []assertion) { - for _, a := range assertions { - assert.Equal(t, api.UbuntuProSettings{GuestAttach: a.expectedSetting}, s.GuestAttachSettings(a.instanceSetting)) - token, err := s.GetGuestToken(ctx, a.instanceSetting) - assert.Equal(t, a.expectedToken, token) - if a.expectErr { - assert.True(t, api.StatusErrorCheck(err, a.expectedErrorCode)) - } else { - assert.NoError(t, err) - } - } - } - - // There is no "interfaces" directory, so the guest attach setting should be off. - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Create the interfaces directory and sleep to wait for the filewatcher to catch up. - interfacesDir := filepath.Join(tmpDir, "interfaces") - err = os.Mkdir(interfacesDir, 0755) - require.NoError(t, err) - sleep() - - // There is no "lxd-config.json" file, so the guest attach setting should be off. - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Create the lxd-config.json file and sleep to wait for the filewatcher. - lxdConfigFilepath := filepath.Join(interfacesDir, "lxd-config.json") - f, err := os.Create(lxdConfigFilepath) - require.NoError(t, err) - err = f.Close() - require.NoError(t, err) - sleep() - - // The guest attach setting should still be false as we've not written anything to the file. - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Write '{"guest_attach":"available"}' to the settings file. - writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingAvailable) - assert.Equal(t, guestAttachSettingAvailable, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) - - // Write '{"guest_attach":"off"}' to the settings file. - writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOff) - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Write '{"guest_attach":"on"}' to the settings file. - writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) - assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) - - // Write invalid JSON to the settings file. - writeSettingsFile(lxdConfigFilepath, "{{}\\foo", "") - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Write '{"guest_attach":"on"}' to the settings file. - writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) - assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) - - // Write an invalid setting to the settings file. - writeSettingsFile(lxdConfigFilepath, "", "foo") - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Write '{"guest_attach":"on"}' to the settings file. - writeSettingsFile(lxdConfigFilepath, "", guestAttachSettingOn) - assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) - - // Remove the config file. - err = os.Remove(lxdConfigFilepath) - require.NoError(t, err) - sleep() - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - // Create a temporary config file and move it to the right location. - tmpSettingsFilePath := filepath.Join(interfacesDir, "lxd-config.json.tmp") - _, err = os.Create(tmpSettingsFilePath) - require.NoError(t, err) - writeSettingsFile(tmpSettingsFilePath, "", guestAttachSettingOn) - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - err = os.Rename(tmpSettingsFilePath, lxdConfigFilepath) - require.NoError(t, err) - sleep() - assert.Equal(t, guestAttachSettingOn, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOnOrAvailable) - - // Cancel the context. - cancel() - sleep() - assert.Equal(t, guestAttachSettingOff, s.guestAttachSetting) - runAssertions(assertionsWhenHostHasGuestAttachmentOff) - - err = os.RemoveAll(tmpDir) - require.NoError(t, err) -} From 345e9b4c82a5453fec471d8948273e6e224b5255 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:45:27 +0000 Subject: [PATCH 06/11] Revert "lxd/state: Add `ubuntupro.Client` to `state.State`." This reverts commit 8e6ce559db46f3d5955525a6eeb10fb287b04c0b. Signed-off-by: Thomas Parrott --- lxd/state/state.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lxd/state/state.go b/lxd/state/state.go index 67092d979941..6d0df265ce7a 100644 --- a/lxd/state/state.go +++ b/lxd/state/state.go @@ -21,7 +21,6 @@ import ( "github.com/canonical/lxd/lxd/maas" "github.com/canonical/lxd/lxd/node" "github.com/canonical/lxd/lxd/sys" - "github.com/canonical/lxd/lxd/ubuntupro" "github.com/canonical/lxd/shared" ) @@ -95,9 +94,6 @@ type State struct { // Authorizer. Authorizer auth.Authorizer - - // Ubuntu pro settings. - UbuntuPro *ubuntupro.Client } // LeaderInfo represents information regarding cluster member leadership. From f69629a630bdca81bc52d590487ba4a7e516a070 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:45:37 +0000 Subject: [PATCH 07/11] Revert "lxd/state: Add `ubuntupro.Client` to `Daemon`." This reverts commit 42e3526cfe7edcebe7bb1f2f9f8415010e7ef901. Signed-off-by: Thomas Parrott --- lxd/daemon.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index 6ba14f70a644..e73b0e515645 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -69,7 +69,6 @@ import ( "github.com/canonical/lxd/lxd/storage/s3/miniod" "github.com/canonical/lxd/lxd/sys" "github.com/canonical/lxd/lxd/task" - "github.com/canonical/lxd/lxd/ubuntupro" "github.com/canonical/lxd/lxd/ucred" "github.com/canonical/lxd/lxd/util" "github.com/canonical/lxd/lxd/warnings" @@ -165,9 +164,6 @@ type Daemon struct { // Syslog listener cancel function. syslogSocketCancel context.CancelFunc - - // Ubuntu Pro settings - ubuntuPro *ubuntupro.Client } // DaemonConfig holds configuration values for Daemon. @@ -607,7 +603,6 @@ func (d *Daemon) State() *state.State { ServerUUID: d.serverUUID, StartTime: d.startTime, Authorizer: d.authorizer, - UbuntuPro: d.ubuntuPro, } s.LeaderInfo = func() (*state.LeaderInfo, error) { @@ -1871,9 +1866,6 @@ func (d *Daemon) init() error { // Start all background tasks d.tasks.Start(d.shutdownCtx) - // Load Ubuntu Pro configuration before starting any instances. - d.ubuntuPro = ubuntupro.New(d.os.ReleaseInfo["NAME"], d.shutdownCtx) - // Restore instances instancesStart(d.State(), instances) From 8125bc7c89ca0fedac2d4ff36447f524406b4c2a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:45:47 +0000 Subject: [PATCH 08/11] Revert "lxd/instance/instancetype: Add `ubuntu_pro.guest_attach` to instance configuration." This reverts commit 7c830613d71eb83eec6dede63707faaf9c9389c7. Signed-off-by: Thomas Parrott --- lxd/instance/instancetype/instance.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lxd/instance/instancetype/instance.go b/lxd/instance/instancetype/instance.go index 5b064217bdcb..e37fd62c1dba 100644 --- a/lxd/instance/instancetype/instance.go +++ b/lxd/instance/instancetype/instance.go @@ -405,20 +405,6 @@ var InstanceConfigKeysAny = map[string]func(value string) error{ return err }, - // lxdmeta:generate(entities=instance; group=miscellaneous; key=ubuntu_pro.guest_attach) - // Indicate whether the guest should auto-attach Ubuntu Pro at start up. - // The allowed values are `off`, `on`, and `available`. - // If set to `off`, it will not be possible for the Ubuntu Pro client in the guest to obtain guest token via `devlxd`. - // If set to `available`, attachment via guest token is possible but will not be performed automatically by the Ubuntu Pro client in the guest at startup. - // If set to `on`, attachment will be performed automatically by the Ubuntu Pro client in the guest at startup. - // To allow guest attachment, the host must be an Ubuntu machine that is Pro attached, and guest attachment must be enabled via the Pro client. - // To do this, run `pro config set lxd_guest_attach=on`. - // --- - // type: string - // liveupdate: no - // shortdesc: Whether to auto-attach Ubuntu Pro. - "ubuntu_pro.guest_attach": validate.Optional(validate.IsOneOf("off", "on", "available")), - // Volatile keys. // lxdmeta:generate(entities=instance; group=volatile; key=volatile.apply_template) From 13808418dee2281d03983b88dfd7dc100dfa4bc0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:45:59 +0000 Subject: [PATCH 09/11] Revert "{doc,lxd/metadata}: Run `make update-metadata`." This reverts commit 05b71522472b1c9a02a36813f11d9e4408d6f558. Signed-off-by: Thomas Parrott --- doc/metadata.txt | 13 ------------- lxd/metadata/configuration.json | 8 -------- 2 files changed, 21 deletions(-) diff --git a/doc/metadata.txt b/doc/metadata.txt index 8acedae509c2..87b72eb3e850 100644 --- a/doc/metadata.txt +++ b/doc/metadata.txt @@ -1840,19 +1840,6 @@ Possible values are `boot` (load the modules when booting the container) and `on ``` -```{config:option} ubuntu_pro.guest_attach instance-miscellaneous -:liveupdate: "no" -:shortdesc: "Whether to auto-attach Ubuntu Pro." -:type: "string" -Indicate whether the guest should auto-attach Ubuntu Pro at start up. -The allowed values are `off`, `on`, and `available`. -If set to `off`, it will not be possible for the Ubuntu Pro client in the guest to obtain guest token via `devlxd`. -If set to `available`, attachment via guest token is possible but will not be performed automatically by the Ubuntu Pro client in the guest at startup. -If set to `on`, attachment will be performed automatically by the Ubuntu Pro client in the guest at startup. -To allow guest attachment, the host must be an Ubuntu machine that is Pro attached, and guest attachment must be enabled via the Pro client. -To do this, run `pro config set lxd_guest_attach=on`. -``` - ```{config:option} user.* instance-miscellaneous :liveupdate: "no" :shortdesc: "Free-form user key/value storage" diff --git a/lxd/metadata/configuration.json b/lxd/metadata/configuration.json index 2c5a5dd1d900..2181fed6f36f 100644 --- a/lxd/metadata/configuration.json +++ b/lxd/metadata/configuration.json @@ -2105,14 +2105,6 @@ "type": "string" } }, - { - "ubuntu_pro.guest_attach": { - "liveupdate": "no", - "longdesc": "Indicate whether the guest should auto-attach Ubuntu Pro at start up.\nThe allowed values are `off`, `on`, and `available`.\nIf set to `off`, it will not be possible for the Ubuntu Pro client in the guest to obtain guest token via `devlxd`.\nIf set to `available`, attachment via guest token is possible but will not be performed automatically by the Ubuntu Pro client in the guest at startup.\nIf set to `on`, attachment will be performed automatically by the Ubuntu Pro client in the guest at startup.\nTo allow guest attachment, the host must be an Ubuntu machine that is Pro attached, and guest attachment must be enabled via the Pro client.\nTo do this, run `pro config set lxd_guest_attach=on`.", - "shortdesc": "Whether to auto-attach Ubuntu Pro.", - "type": "string" - } - }, { "user.*": { "liveupdate": "no", From 6d8c8fa73e352281331422b9aaec9c75b2aa8c0d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:46:07 +0000 Subject: [PATCH 10/11] Revert "lxd: Add Ubuntu Pro handlers to devlxd." This reverts commit d62691f74612ebd727f6bd4005c82060b3ca675b. Signed-off-by: Thomas Parrott --- lxd/devlxd.go | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/lxd/devlxd.go b/lxd/devlxd.go index efdb7df9a28a..ec7411646a89 100644 --- a/lxd/devlxd.go +++ b/lxd/devlxd.go @@ -293,50 +293,6 @@ func devlxdDevicesGetHandler(d *Daemon, c instance.Instance, w http.ResponseWrit return response.DevLxdResponse(http.StatusOK, c.ExpandedDevices(), "json", c.Type() == instancetype.VM) } -var devlxdUbuntuProGet = devLxdHandler{ - path: "/1.0/ubuntu-pro", - handlerFunc: devlxdUbuntuProGetHandler, -} - -func devlxdUbuntuProGetHandler(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) response.Response { - if shared.IsFalse(c.ExpandedConfig()["security.devlxd"]) { - return response.DevLxdErrorResponse(api.NewGenericStatusError(http.StatusForbidden), c.Type() == instancetype.VM) - } - - if r.Method != http.MethodGet { - return response.DevLxdErrorResponse(api.NewGenericStatusError(http.StatusMethodNotAllowed), c.Type() == instancetype.VM) - } - - settings := d.State().UbuntuPro.GuestAttachSettings(c.ExpandedConfig()["ubuntu_pro.guest_attach"]) - - // Otherwise, return the value from the instance configuration. - return response.DevLxdResponse(http.StatusOK, settings, "json", c.Type() == instancetype.VM) -} - -var devlxdUbuntuProTokenPost = devLxdHandler{ - path: "/1.0/ubuntu-pro/token", - handlerFunc: devlxdUbuntuProTokenPostHandler, -} - -func devlxdUbuntuProTokenPostHandler(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) response.Response { - if shared.IsFalse(c.ExpandedConfig()["security.devlxd"]) { - return response.DevLxdErrorResponse(api.NewGenericStatusError(http.StatusForbidden), c.Type() == instancetype.VM) - } - - if r.Method != http.MethodPost { - return response.DevLxdErrorResponse(api.NewGenericStatusError(http.StatusMethodNotAllowed), c.Type() == instancetype.VM) - } - - // Return http.StatusForbidden if the host does not have guest attachment enabled. - tokenJSON, err := d.State().UbuntuPro.GetGuestToken(r.Context(), c.ExpandedConfig()["ubuntu_pro.guest_attach"]) - if err != nil { - return response.DevLxdErrorResponse(fmt.Errorf("Failed to get an Ubuntu Pro guest token: %w", err), c.Type() == instancetype.VM) - } - - // Pass it back to the guest. - return response.DevLxdResponse(http.StatusOK, tokenJSON, "json", c.Type() == instancetype.VM) -} - var handlers = []devLxdHandler{ { path: "/", @@ -351,8 +307,6 @@ var handlers = []devLxdHandler{ devlxdEventsGet, devlxdImageExport, devlxdDevicesGet, - devlxdUbuntuProGet, - devlxdUbuntuProTokenPost, } func hoistReq(f func(*Daemon, instance.Instance, http.ResponseWriter, *http.Request) response.Response, d *Daemon) func(http.ResponseWriter, *http.Request) { From 6671ca9577c09bd57aacceaa94217e094acdb4c6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 19 Nov 2024 14:46:15 +0000 Subject: [PATCH 11/11] Revert "lxd-agent: Add Ubuntu Pro handlers to devlxd." This reverts commit f98c7ad6b95c40bc080882ab844dc5ee99776113. Signed-off-by: Thomas Parrott --- lxd-agent/devlxd.go | 93 --------------------------------------------- 1 file changed, 93 deletions(-) diff --git a/lxd-agent/devlxd.go b/lxd-agent/devlxd.go index cff186de220d..dd64bcd9cda5 100644 --- a/lxd-agent/devlxd.go +++ b/lxd-agent/devlxd.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "fmt" "io" "net" @@ -298,96 +297,6 @@ func devlxdImageExportHandler(d *Daemon, w http.ResponseWriter, r *http.Request) return nil } -var devlxdUbuntuProGet = devLxdHandler{ - path: "/1.0/ubuntu-pro", - handlerFunc: devlxdUbuntuProGetHandler, -} - -func devlxdUbuntuProGetHandler(d *Daemon, w http.ResponseWriter, r *http.Request) *devLxdResponse { - if r.Method != http.MethodGet { - return errorResponse(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed)) - } - - // Get a http.Client. - client, err := getClient(d.serverCID, int(d.serverPort), d.serverCertificate) - if err != nil { - return smartResponse(fmt.Errorf("Failed connecting to LXD over vsock: %w", err)) - } - - // Remove the request URI, this cannot be set on requests. - r.RequestURI = "" - - // Set up the request URL with the correct host. - r.URL = &api.NewURL().Scheme("https").Host("custom.socket").Path(version.APIVersion, "ubuntu-pro").URL - - // Proxy the request. - resp, err := client.Do(r) - if err != nil { - return errorResponse(http.StatusInternalServerError, err.Error()) - } - - var apiResponse api.Response - err = json.NewDecoder(resp.Body).Decode(&apiResponse) - if err != nil { - return smartResponse(err) - } - - var settingsResponse api.UbuntuProSettings - err = json.Unmarshal(apiResponse.Metadata, &settingsResponse) - if err != nil { - return errorResponse(http.StatusInternalServerError, fmt.Sprintf("Invalid Ubuntu Token settings response received from host: %v", err)) - } - - return okResponse(settingsResponse, "json") -} - -var devlxdUbuntuProTokenPost = devLxdHandler{ - path: "/1.0/ubuntu-pro/token", - handlerFunc: devlxdUbuntuProTokenPostHandler, -} - -func devlxdUbuntuProTokenPostHandler(d *Daemon, w http.ResponseWriter, r *http.Request) *devLxdResponse { - if r.Method != http.MethodPost { - return errorResponse(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed)) - } - - // Get a http.Client. - client, err := getClient(d.serverCID, int(d.serverPort), d.serverCertificate) - if err != nil { - return smartResponse(fmt.Errorf("Failed connecting to LXD over vsock: %w", err)) - } - - // Remove the request URI, this cannot be set on requests. - r.RequestURI = "" - - // Set up the request URL with the correct host. - r.URL = &api.NewURL().Scheme("https").Host("custom.socket").Path(version.APIVersion, "ubuntu-pro", "token").URL - - // Proxy the request. - resp, err := client.Do(r) - if err != nil { - return errorResponse(http.StatusInternalServerError, err.Error()) - } - - var apiResponse api.Response - err = json.NewDecoder(resp.Body).Decode(&apiResponse) - if err != nil { - return smartResponse(err) - } - - if apiResponse.StatusCode != http.StatusOK { - return errorResponse(apiResponse.Code, apiResponse.Error) - } - - var tokenResponse api.UbuntuProGuestTokenResponse - err = json.Unmarshal(apiResponse.Metadata, &tokenResponse) - if err != nil { - return errorResponse(http.StatusInternalServerError, fmt.Sprintf("Invalid Ubuntu Token response received from host: %v", err)) - } - - return okResponse(tokenResponse, "json") -} - var handlers = []devLxdHandler{ { path: "/", @@ -402,8 +311,6 @@ var handlers = []devLxdHandler{ devLxdEventsGet, devlxdDevicesGet, devlxdImageExport, - devlxdUbuntuProGet, - devlxdUbuntuProTokenPost, } func hoistReq(f func(*Daemon, http.ResponseWriter, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {