Skip to content

Commit

Permalink
feat(Landscape): Hostagent URL moved to Landscape config file (#365)
Browse files Browse the repository at this point in the history
The landscape client config currently needs at least three fields:

```
[client]
url = https://landscape.canonical.com/message-system
ping_url = http://landscape.canonical.com/ping
account_name = dummy
```

There is another registry key right now called LandscapeAgentURL which
is used for the Windows-Agent gRPC connection. It would make more sense
to put this information in the config as well, for example:

```
[host]
url = https://landscape.canonical.com/hostagent

[client]
url = https://landscape.canonical.com/message-system
ping_url = http://landscape.canonical.com/ping
account_name = dummy
```

The host section is removed before writing the file to
`/etc/landscape/client.conf`.

---

In the future, we'll have to change the default value of `host/url`. It
defaults to empty string both before and after this PR. We'd want it to
default to Canonical's URL. This has been captured in Jira card 1687.

---

UDENG-1644
  • Loading branch information
EduardGomezEscandell authored Oct 27, 2023
2 parents ccefff6 + 4de08ba commit e71aab4
Show file tree
Hide file tree
Showing 18 changed files with 131 additions and 102 deletions.
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
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")
}

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")

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

0 comments on commit e71aab4

Please sign in to comment.