Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Landscape): Hostagent URL moved to Landscape config file #365

Merged
merged 10 commits into from
Oct 27, 2023
37 changes: 37 additions & 0 deletions docs/03-restart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# How to restart Ubuntu Pro for Windows
didrocks marked this conversation as resolved.
Show resolved Hide resolved
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.
26 changes: 0 additions & 26 deletions docs/04-attach-landscape.md

This file was deleted.

4 changes: 4 additions & 0 deletions docs/03-enable-pro.md → docs/04-enable-pro.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
19 changes: 19 additions & 0 deletions docs/05-attach-landscape.md
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 5 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
```
23 changes: 0 additions & 23 deletions windows-agent/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const (
registryPath = `Software\Canonical\UbuntuPro`

fieldLandscapeClientConfig = "LandscapeClientConfig"
fieldLandscapeAgentURL = "LandscapeAgentURL"
fieldLandscapeAgentUID = "LandscapeAgentUID"
)

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
23 changes: 2 additions & 21 deletions windows-agent/internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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"] = ""
}
Expand Down
32 changes: 30 additions & 2 deletions windows-agent/internal/proservices/landscape/landscape.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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) {
Expand All @@ -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")
didrocks marked this conversation as resolved.
Show resolved Hide resolved
}

defer func() {
go c.keepConnected(ctx, address)
Expand Down
40 changes: 14 additions & 26 deletions windows-agent/internal/proservices/landscape/landscape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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},
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions wsl-pro-service/internal/system/landscape.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
didrocks marked this conversation as resolved.
Show resolved Hide resolved

distroName, err := s.wslDistroName(ctx)
if err != nil {
return "", err
Expand Down
Loading
Loading