diff --git a/docs/03-restart.md b/docs/03-restart.md new file mode 100644 index 000000000..dc6752cb2 --- /dev/null +++ b/docs/03-restart.md @@ -0,0 +1,37 @@ +# How to restart Ubuntu Pro for Windows +Some configuration changes only apply when you restart Ubuntu Pro for Windows. Here is a guide on how to restart it. + +## Option 1: Restart your machine +This is the simple one. If you're not in a hurry to see the configuration updated, just wait until next time you boot your machine. + +## Option 2: Restart only Ubuntu Pro For Windows +1. Stop the agent: + ```powershell + Get-Process -Name Ubuntu-Pro-Agent | Stop-Process + ``` +2. Stop the distro, or distros you installed WSL-Pro-Service in: + ```powershell + wsl --terminate DISTRO_NAME_1 + wsl --terminate DISTRO_NAME_2 + # etc. + + # Alternatively, stop all distros: + wsl --shutdown + ``` +7. Start the agent again: + 1. Open the start Menu and search for "Ubuntu Pro For Windows". + 2. The GUI should start. + 3. Wait a minute. + 4. Click on "Click to restart it". +8. Start the distro, or distros you installed WSL-Pro-Service in. + +# How to reset Ubuntu Pro for Windows back to factory settings +If you want to restart UP4W with factory settings, do: +1. Stop the agent and WSL: + ```powershell + Get-Process -Name "Ubuntu-Pro-Agent" | Stop-Process` + wsl --shutdown + ``` +1. Remove folder `$env:LocalAppData\UbuntuPro`. +2. Remove registry key `HKEY_CURRENT_USER\Software\Canonical\UbuntuPro`. +3. You're done. Next time you start the GUI it'll be like a fresh install. \ No newline at end of file diff --git a/docs/04-attach-landscape.md b/docs/04-attach-landscape.md deleted file mode 100644 index 107ef6bfa..000000000 --- a/docs/04-attach-landscape.md +++ /dev/null @@ -1,26 +0,0 @@ -# How to auto-register WSL distros to Landscape with UP4W -You can use a private Landscape instance (different from [landscape.canonical.com](https://landscape.canonical.com)). It must be over HTTP, as using certificates is not yet supported. To do so, follow these steps: -1. Press Windows+R. -2. Write `regedit.exe` and enter. -3. Go to `HKEY_CURRENT_USER\Software\Canonical\UbuntuPro`. -4. There are two relevant fields: - - `LandscapeAgentURL` should contain the URL where the Landscape Host-agent server is hosted. - - `LandscapeClientConfig` should contain the contents of the YAML file with the settings, such as the [example from the Landscape repository](https://github.com/canonical/landscape-client/blob/master/example.conf). -5. To edit any of the fields, right-click and Edit. -6. If you need more than one line, delete the field and create a new one with the same name, and type `Multi-String Value`. -7. The changes will take effect next time you start the machine. If you want them to be applied now, follow the next steps. Otherwise, you're done. All new distros will automatically attach to Landscape. -8. Stop the agent: - ```powershell - Get-Process -Name Ubuntu-Pro-Agent | Stop-Process - ``` -9. Start the agent again: - 1. Open the start Menu and search for "Ubuntu Pro For Windows". - 2. The GUI should start. - 3. Wait a minute. - 4. Click on "Click to restart it". -10. Stop the distro you installed WSL-Pro-Service in: - ```powershell - wsl --terminate DISTRO_NAME - ``` -11. Start the distro you installed WSL-Pro-Service in. -12. You should see a new "pending computer authorisation" in your Landscape dashboard. \ No newline at end of file diff --git a/docs/03-enable-pro.md b/docs/04-enable-pro.md similarity index 59% rename from docs/03-enable-pro.md rename to docs/04-enable-pro.md index e4c308195..aef66a0c8 100644 --- a/docs/03-enable-pro.md +++ b/docs/04-enable-pro.md @@ -10,3 +10,7 @@ pro status ``` +## Organisational pro enablement +1. Find registry key `HKEY_CURRENT_USER\Software\Canonical\UbuntuPro`, field `ProTokenOrg`. +2. Write your Ubuntu Pro Token into the registry key +3. The changes will take effect next time you start Ubuntu Pro For Windows. All new distros will automatically become pro-enabled. If you want them to be applied now, follow the steps on how to restart Ubuntu Pro For Windows. Otherwise, you're done. \ No newline at end of file diff --git a/docs/05-attach-landscape.md b/docs/05-attach-landscape.md new file mode 100644 index 000000000..5bd1b82f0 --- /dev/null +++ b/docs/05-attach-landscape.md @@ -0,0 +1,19 @@ +# How to auto-register WSL distros to Landscape with UP4W +You can use a private Landscape instance (different from [landscape.canonical.com](https://landscape.canonical.com)). It must be over HTTP, as using certificates is not yet supported. To do so, follow these steps: +1. Find registry key `HKEY_CURRENT_USER\Software\Canonical\UbuntuPro`, field `LandscapeClientConfig`. +2. Copy the contents of the Landscape configuration file into the registry key: + ```ini + [host] + # The main URL for the landscape server to connect this client to. If you + # purchased a Landscape Dedicated Server (LDS), change this to point to your + # server instead. This needs to point to the message-system URL. + # + # Please pay special attention to the protocol used here, since it is a common + # source of error. + url = https://landscape.canonical.com/TODO-HOSTAGENT-ENDPOINT + + [client] + # The configuration for the WSL client. See an example here + # https://github.com/canonical/landscape-client/blob/master/example.conf + ``` +3. The changes will take effect next time you start Ubuntu Pro For Windows. All new distros will automatically become landscape-enabled. If you want them to be applied now, follow the steps on how to restart Ubuntu Pro For Windows. Otherwise, you're done. diff --git a/docs/05-windows-agent-command-line-reference.md b/docs/06-windows-agent-command-line-reference.md similarity index 100% rename from docs/05-windows-agent-command-line-reference.md rename to docs/06-windows-agent-command-line-reference.md diff --git a/docs/06-wsl-pro-service-command-line-reference.md b/docs/07-wsl-pro-service-command-line-reference.md similarity index 100% rename from docs/06-wsl-pro-service-command-line-reference.md rename to docs/07-wsl-pro-service-command-line-reference.md diff --git a/docs/index.md b/docs/index.md index f9492904f..60998d90c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,8 +19,9 @@ Thinking about using Ubuntu Pro for Windows for your next project? Get in touch! :hidden: Install <02-install> -Auto-enable Ubuntu Pro <03-enable-pro> -Auto-attach distros to Landscape <04-attach-landscape> -Windows Agent command line interface <05-windows-agent-command-line-reference> -WSL Pro Service command line interface <06-wsl-pro-service-command-line-reference> +Restart Ubuntu Pro For Windows <03-restart> +Auto-enable Ubuntu Pro <04-enable-pro> +Auto-attach distros to Landscape <05-attach-landscape> +Windows Agent command line interface <06-windows-agent-command-line-reference> +WSL Pro Service command line interface <07-wsl-pro-service-command-line-reference> ``` diff --git a/windows-agent/internal/config/config.go b/windows-agent/internal/config/config.go index 61eab7e45..d4b948ffe 100644 --- a/windows-agent/internal/config/config.go +++ b/windows-agent/internal/config/config.go @@ -26,7 +26,6 @@ const ( registryPath = `Software\Canonical\UbuntuPro` fieldLandscapeClientConfig = "LandscapeClientConfig" - fieldLandscapeAgentURL = "LandscapeAgentURL" fieldLandscapeAgentUID = "LandscapeAgentUID" ) @@ -61,7 +60,6 @@ type Config struct { // configData is a bag of data unrelated to the subscription status. type configData struct { landscapeClientConfig string - landscapeAgentURL string landscapeAgentUID string } @@ -221,18 +219,6 @@ func (c *Config) SetSubscription(ctx context.Context, proToken string, source Su return nil } -// LandscapeAgentURL returns the value of the landscape server URL. -func (c *Config) LandscapeAgentURL(ctx context.Context) (string, error) { - c.mu.Lock() - defer c.mu.Unlock() - - if err := c.load(ctx); err != nil { - return "", fmt.Errorf("could not load: %v", err) - } - - return c.data.landscapeAgentURL, nil -} - // LandscapeClientConfig returns the value of the landscape server URL. func (c *Config) LandscapeClientConfig(ctx context.Context) (string, error) { c.mu.Lock() @@ -329,11 +315,6 @@ func (c *Config) loadRegistry(ctx context.Context) (proTokens map[SubscriptionSo return nil, data, err } - data.landscapeAgentURL, err = c.readValue(ctx, k, fieldLandscapeAgentURL) - if err != nil { - return proTokens, data, err - } - data.landscapeClientConfig, err = c.readValue(ctx, k, fieldLandscapeClientConfig) if err != nil { return proTokens, data, err @@ -376,10 +357,6 @@ func (c *Config) dump() (err error) { } } - if err := c.registry.WriteValue(k, fieldLandscapeAgentURL, c.data.landscapeAgentURL); err != nil { - return fmt.Errorf("could not write into registry key: %v", err) - } - if err := c.registry.WriteMultilineValue(k, fieldLandscapeClientConfig, c.data.landscapeClientConfig); err != nil { return fmt.Errorf("could not write into registry key: %v", err) } diff --git a/windows-agent/internal/config/config_test.go b/windows-agent/internal/config/config_test.go index 7298d13cc..f28f2a7eb 100644 --- a/windows-agent/internal/config/config_test.go +++ b/windows-agent/internal/config/config_test.go @@ -31,16 +31,14 @@ const ( orgTokenExists = keyExists | 1<<(iota+2) // Key exists, organization token field exists userTokenExists // Key exists, user token field exists storeTokenExists // Key exists, microsoft store token field exists - landscapeAgentURLExists // Key exists, landscape agent URL field exists landscapeClientConfigExists // Key exists, landscape client config field exists landscapeAgentUIDExists // Key exists, landscape agent UID field exists orgTokenHasValue = orgTokenExists | 1<<16 // Key exists, organization token field exists and is not empty userTokenHasValue = userTokenExists | 1<<17 // Key exists, user token field exists and is not empty storeTokenHasValue = storeTokenExists | 1<<18 // Key exists, microsoft store token field exists and is not empty - landscapeAgentURLHasValue = landscapeAgentURLExists | 1<<19 // Key exists, landscape agent URL field exists and is not empty - landscapeClientConfigHasValue = landscapeClientConfigExists | 1<<20 // Key exists, landscape client config field exists and is not empty - landscapeAgentUIDHasValue = landscapeAgentUIDExists | 1<<21 // Key exists, landscape agent UID field exists and is not empty + landscapeClientConfigHasValue = landscapeClientConfigExists | 1<<19 // Key exists, landscape client config field exists and is not empty + landscapeAgentUIDHasValue = landscapeAgentUIDExists | 1<<20 // Key exists, landscape agent UID field exists and is not empty ) func TestSubscription(t *testing.T) { @@ -95,16 +93,6 @@ func TestSubscription(t *testing.T) { } } -func TestLandscapeAgentURL(t *testing.T) { - t.Parallel() - testConfigGetter(t, testConfigGetterSettings{ - getter: (*config.Config).LandscapeAgentURL, - getterName: "LandscapeAgentURL", - registryHasValue: landscapeAgentURLHasValue, - want: "www.example.com/registry-example", - }) -} - func TestLandscapeClientConfig(t *testing.T) { t.Parallel() @@ -560,13 +548,6 @@ func setUpMockRegistry(mockErrors uint32, state registryState, readOnly bool) *r r.UbuntuProData["ProTokenStore"] = "store_token" } - if state.is(landscapeAgentURLExists) { - r.UbuntuProData["LandscapeAgentURL"] = "" - } - if state.is(landscapeAgentURLHasValue) { - r.UbuntuProData["LandscapeAgentURL"] = "www.example.com/registry-example" - } - if state.is(landscapeClientConfigExists) { r.UbuntuProData["LandscapeClientConfig"] = "" } diff --git a/windows-agent/internal/proservices/landscape/landscape.go b/windows-agent/internal/proservices/landscape/landscape.go index 6cb46d058..ceb0f6a02 100644 --- a/windows-agent/internal/proservices/landscape/landscape.go +++ b/windows-agent/internal/proservices/landscape/landscape.go @@ -55,7 +55,6 @@ type connection struct { // Config is a configuration provider for ProToken and the Landscape URL. type Config interface { LandscapeClientConfig(context.Context) (string, error) - LandscapeAgentURL(context.Context) (string, error) Subscription(context.Context) (string, config.SubscriptionSource, error) @@ -96,6 +95,32 @@ func NewClient(conf Config, db *database.DistroDB, args ...Option) (*Client, err return c, nil } +// hostagentURL parses the landscape config file to find the hostagent URL. +func (c *Client) hostagentURL(ctx context.Context) (string, error) { + config, err := c.conf.LandscapeClientConfig(ctx) + if err != nil { + return "", err + } + + r := strings.NewReader(config) + data, err := ini.Load(r) + if err != nil { + return "", fmt.Errorf("could not parse config: %v", err) + } + + s, err := data.GetSection("host") + if err != nil { // Section does not exist + return "", nil + } + + k, err := s.GetKey("url") + if err != nil { // Key does not exist + return "", nil + } + + return k.String(), nil +} + // Connect starts the connection and starts talking to the server. // Call disconnect to deallocate resources. func (c *Client) Connect(ctx context.Context) (err error) { @@ -108,10 +133,13 @@ func (c *Client) Connect(ctx context.Context) (err error) { // Dummy connection to indicate that a first attempt was attempted c.conn = &connection{} - address, err := c.conf.LandscapeAgentURL(ctx) + address, err := c.hostagentURL(ctx) if err != nil { return err } + if address == "" { + return errors.New("no hostagent URL provided in the Landscape configuration") + } defer func() { go c.keepConnected(ctx, address) diff --git a/windows-agent/internal/proservices/landscape/landscape_test.go b/windows-agent/internal/proservices/landscape/landscape_test.go index 73d6960f4..d6fc1419a 100644 --- a/windows-agent/internal/proservices/landscape/landscape_test.go +++ b/windows-agent/internal/proservices/landscape/landscape_test.go @@ -51,8 +51,8 @@ func TestConnect(t *testing.T) { landscapeUIDReadErr bool landscapeUIDWriteErr bool - landscapeURLErr bool - tokenErr bool + noLandscapeURL bool + tokenErr bool requireCertificate bool breakLandscapeClientConfig bool @@ -69,7 +69,7 @@ func TestConnect(t *testing.T) { "Success with an SSL certificate": {requireCertificate: true}, "Error when the context is cancelled before Connected": {precancelContext: true, wantErr: true}, - "Error when the landscape URL cannot be retrieved": {landscapeURLErr: true, wantErr: true}, + "Error when the landscape URL cannot be retrieved": {noLandscapeURL: true, wantErr: true}, "Error when the landscape UID cannot be retrieved": {landscapeUIDReadErr: true, wantErr: true}, "Error when the landscape UID cannot be stored": {landscapeUIDWriteErr: true, wantErr: true}, "Error when the server cannot be reached": {serverNotAvailable: true, wantErr: true}, @@ -93,15 +93,11 @@ func TestConnect(t *testing.T) { defer lis.Close() conf := &mockConfig{ - proToken: "TOKEN", - landscapeAgentURL: lis.Addr().String(), + proToken: "TOKEN", // We trigger an error on first-contact SendUpdatedInfo by erroring out in conf.ProToken() proTokenErr: tc.tokenErr, - // We trigger an earlier error by erroring out on LandscapeAgentURL - landscapeURLErr: tc.landscapeURLErr, - // We trigger errors trying to read or write to/from the registry landscapeUIDErr: tc.landscapeUIDReadErr, setLandscapeUIDErr: tc.landscapeUIDWriteErr, @@ -119,7 +115,11 @@ func TestConnect(t *testing.T) { err = nil } require.NoError(t, err, "Setup: could not load landscape config") + conf.landscapeClientConfig = string(out) + if !tc.noLandscapeURL { + conf.landscapeClientConfig = fmt.Sprintf("[host]\nurl=%q\n\n%s", lis.Addr(), conf.landscapeClientConfig) + } if !tc.serverNotAvailable { //nolint:errcheck // We don't care about these errors @@ -218,8 +218,8 @@ func TestSendUpdatedInfo(t *testing.T) { lis, server, mockService := setUpLandscapeMock(t, ctx, "localhost:", false) conf := &mockConfig{ - proToken: "TOKEN", - landscapeAgentURL: lis.Addr().String(), + proToken: "TOKEN", + landscapeClientConfig: fmt.Sprintf("[host]\nurl=%q\n", lis.Addr()), } //nolint:errcheck // We don't care about these errors @@ -386,8 +386,8 @@ func TestAutoReconnection(t *testing.T) { defer server.Stop() conf := &mockConfig{ - proToken: "TOKEN", - landscapeAgentURL: lis.Addr().String(), + proToken: "TOKEN", + landscapeClientConfig: fmt.Sprintf("[host]\nurl=%q\n", lis.Addr()), } db, err := database.New(ctx, t.TempDir(), conf) @@ -522,8 +522,8 @@ func TestReceiveCommands(t *testing.T) { defer server.Stop() conf := &mockConfig{ - proToken: "TOKEN", - landscapeAgentURL: lis.Addr().String(), + proToken: "TOKEN", + landscapeClientConfig: fmt.Sprintf("[host]\nurl=%q\n", lis.Addr()), } db, err := database.New(ctx, t.TempDir(), conf) @@ -787,12 +787,10 @@ func setUpLandscapeMock(t *testing.T, ctx context.Context, addr string, requireC type mockConfig struct { proToken string - landscapeAgentURL string landscapeClientConfig string landscapeAgentUID string proTokenErr bool - landscapeURLErr bool landscapeConfigErr bool landscapeUIDErr bool setLandscapeUIDErr bool @@ -824,16 +822,6 @@ func (m *mockConfig) Subscription(ctx context.Context) (string, config.Subscript return m.proToken, config.SubscriptionUser, nil } -func (m *mockConfig) LandscapeAgentURL(ctx context.Context) (string, error) { - m.mu.Lock() - defer m.mu.Unlock() - - if m.landscapeURLErr { - return "", errors.New("Mock error") - } - return m.landscapeAgentURL, nil -} - func (m *mockConfig) LandscapeAgentUID(ctx context.Context) (string, error) { m.mu.Lock() defer m.mu.Unlock() diff --git a/wsl-pro-service/internal/system/landscape.go b/wsl-pro-service/internal/system/landscape.go index 6589d5331..099247051 100644 --- a/wsl-pro-service/internal/system/landscape.go +++ b/wsl-pro-service/internal/system/landscape.go @@ -87,6 +87,8 @@ func modifyConfig(ctx context.Context, s *System, landscapeConfig string, hostag return "", fmt.Errorf("could not parse config: %v", err) } + data.DeleteSection("host") + distroName, err := s.wslDistroName(ctx) if err != nil { return "", err diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_failing_to_override_the_ssl_certficate_path/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_failing_to_override_the_ssl_certficate_path/landscape.conf index f256eee6e..75c264a41 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_failing_to_override_the_ssl_certficate_path/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_failing_to_override_the_ssl_certficate_path/landscape.conf @@ -1,3 +1,6 @@ +[host] +url = www.example.com + [client] hello = world ssl_public_key = D:\Users\TestUser\certificate \ No newline at end of file diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_config_file_cannot_be_written/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_config_file_cannot_be_written/landscape.conf index 41d49280c..c75b99ea4 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_config_file_cannot_be_written/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_config_file_cannot_be_written/landscape.conf @@ -1,2 +1,5 @@ +[host] +url = www.example.com + [client] hello = world \ No newline at end of file diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_landscape-config_command_fails/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_landscape-config_command_fails/landscape.conf index 41d49280c..c75b99ea4 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_landscape-config_command_fails/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/error_when_the_landscape-config_command_fails/landscape.conf @@ -1,2 +1,5 @@ +[host] +url = www.example.com + [client] hello = world \ No newline at end of file diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success/landscape.conf index 41d49280c..c75b99ea4 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success/landscape.conf @@ -1,2 +1,5 @@ +[host] +url = www.example.com + [client] hello = world \ No newline at end of file diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_computer_title/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_computer_title/landscape.conf index 974df0251..94a5422a7 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_computer_title/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_computer_title/landscape.conf @@ -1,3 +1,6 @@ +[host] +url = www.example.com + [client] computer_title = DEFAULT_COMPUTER_TITLE hello = world diff --git a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_the_ssl_certficate_path/landscape.conf b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_the_ssl_certficate_path/landscape.conf index f256eee6e..75c264a41 100644 --- a/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_the_ssl_certficate_path/landscape.conf +++ b/wsl-pro-service/internal/system/testdata/TestLandscapeEnable/success_overriding_the_ssl_certficate_path/landscape.conf @@ -1,3 +1,6 @@ +[host] +url = www.example.com + [client] hello = world ssl_public_key = D:\Users\TestUser\certificate \ No newline at end of file