Skip to content

Commit 9ddfa42

Browse files
authored
Merge pull request #16 from babbageclunk/no-sudo-required
#16 ## Description of change\r \r We already sudo to run ssh to other controller nodes (for access to system-identity) and to edit config files and symlinks locally. This changes the code that reads creds from the machine agent config file to use sudo as well.\r \r ## QA steps\r \r Bootstrap a controller, take a backup, scp that and juju-restore to the primary controller machine.\r Run `./juju-restore juju-backup-<timestamp>.tar.gz` - it should successfully get the credentials to connect to the database without requiring the user to run it under sudo.\r (Make sure there isn't an old restore.log owned by root in the current dir.)\r
2 parents 57952fc + b79dcaa commit 9ddfa42

3 files changed

Lines changed: 109 additions & 55 deletions

File tree

cmd/messages.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ import (
1111
const (
1212
restoreDoc = `
1313
14-
juju-restore must be executed on the MongoDB primary host of a Juju
15-
controller.
14+
juju-restore must be executed on the MongoDB primary host of a Juju controller.
1615
17-
The command will check the state of the target database and the
18-
details of the backup file provided, and restore the contents of the
19-
backup into the controller database.
16+
The command will check the state of the target database and the details of the
17+
backup file provided, and restore the contents of the backup into the
18+
controller database.
2019
2120
`
2221

@@ -26,11 +25,9 @@ Running on primary HA node ✓
2625
`
2726

2827
releaseAgentsControl = `
29-
This controller is in HA and to restore into it successfully,
30-
'juju-restore' needs to manage Juju and Mongo agents on
31-
secondary controller nodes.
32-
However, on the bigger systems, the operator might want to manage
33-
these agents manually.
28+
This controller is in HA and to restore into it successfully, 'juju-restore'
29+
needs to manage Juju and Mongo agents on secondary controller nodes.
30+
However on bigger systems the user might want to manage these agents manually.
3431
3532
Do you want 'juju-restore' to manage these agents automatically? (y/N): `
3633

@@ -39,8 +36,11 @@ Do you want 'juju-restore' to manage these agents automatically? (y/N): `
3936
`
4037

4138
backupFileTemplate = `
42-
You are about to restore a controller from a backup file taken on {{.BackupDate}}.
43-
It contains a controller {{.ControllerModelUUID}} at Juju version {{.BackupJujuVersion}} with {{.ModelCount}} models.
39+
You are about to restore this backup:
40+
Created at: {{.BackupDate}}
41+
Controller: {{.ControllerModelUUID}}
42+
Juju version: {{.BackupJujuVersion}}
43+
Models: {{.ModelCount}}
4444
`
4545

4646
preChecksCompleted = `
@@ -53,7 +53,7 @@ Are you sure you want to proceed? (y/N): `
5353
secondaryAgentsMustStop = `
5454
Juju agents on secondary controller machines must be stopped by this point.
5555
To stop the agents, login into each secondary controller and run:
56-
$ systemctl stop jujud-machine-*
56+
$ sudo systemctl stop jujud-machine-*
5757
`
5858
)
5959

cmd/restore.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
package cmd
55

66
import (
7+
"bytes"
78
"fmt"
9+
"os/exec"
810
"path/filepath"
911

1012
"github.com/juju/cmd"
1113
"github.com/juju/errors"
1214
"github.com/juju/gnuflag"
1315
"github.com/juju/loggo"
14-
"github.com/juju/utils"
16+
"gopkg.in/yaml.v2"
1517

1618
"github.com/juju/juju-restore/core"
1719
"github.com/juju/juju-restore/db"
@@ -282,12 +284,12 @@ const agentConfPattern = "/var/lib/juju/agents/machine-*/agent.conf"
282284
// ReadCredsFromAgentConf tries to load a mongo username and password
283285
// from the standard agent.conf location on a controller machine.
284286
func ReadCredsFromAgentConf() (string, string, error) {
285-
return ReadCredsFromPattern(agentConfPattern)
287+
return ReadCredsFromPattern(agentConfPattern, readFileWithSudo)
286288
}
287289

288290
// ReadCredsFromPattern tries to load a mongo username and password
289291
// from the first file it finds matching the pattern passed in.
290-
func ReadCredsFromPattern(pattern string) (string, string, error) {
292+
func ReadCredsFromPattern(pattern string, readFile func(string) ([]byte, error)) (string, string, error) {
291293
matches, err := filepath.Glob(pattern)
292294
if err != nil {
293295
return "", "", errors.Trace(err)
@@ -301,9 +303,14 @@ func ReadCredsFromPattern(pattern string) (string, string, error) {
301303
Username string `yaml:"tag"`
302304
Password string `yaml:"statepassword"`
303305
}
304-
err = utils.ReadYaml(conf, &creds)
306+
307+
data, err := readFile(conf)
308+
if err != nil {
309+
return "", "", errors.Annotatef(err, "reading %q with sudo", conf)
310+
}
311+
err = yaml.Unmarshal(data, &creds)
305312
if err != nil {
306-
return "", "", errors.Annotatef(err, "reading %q", conf)
313+
return "", "", errors.Annotatef(err, "unmarshalling %q", conf)
307314
}
308315

309316
if creds.Username == "" {
@@ -315,3 +322,14 @@ func ReadCredsFromPattern(pattern string) (string, string, error) {
315322

316323
return creds.Username, creds.Password, nil
317324
}
325+
326+
func readFileWithSudo(path string) ([]byte, error) {
327+
command := exec.Command("sudo", "cat", path)
328+
var out, cmdErr bytes.Buffer
329+
command.Stdout = &out
330+
command.Stderr = &cmdErr
331+
if err := command.Run(); err != nil {
332+
return nil, errors.Trace(err)
333+
}
334+
return out.Bytes(), nil
335+
}

cmd/restore_test.go

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,11 @@ Checking database and replica set health...
148148
Replica set is healthy ✓
149149
Running on primary HA node ✓
150150
151-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
152-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
151+
You are about to restore this backup:
152+
Created at: 2020-03-17 16:28:24 +0000 UTC
153+
Controller: how-bizarre
154+
Juju version: 2.7.5
155+
Models: 3
153156
154157
All restore pre-checks are completed.
155158
@@ -198,8 +201,11 @@ Checking database and replica set health...
198201
Replica set is healthy ✓
199202
Running on primary HA node ✓
200203
201-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
202-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
204+
You are about to restore this backup:
205+
Created at: 2020-03-17 16:28:24 +0000 UTC
206+
Controller: how-bizarre
207+
Juju version: 2.7.5
208+
Models: 3
203209
204210
All restore pre-checks are completed.
205211
@@ -263,14 +269,15 @@ Checking database and replica set health...
263269
Replica set is healthy ✓
264270
Running on primary HA node ✓
265271
266-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
267-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
272+
You are about to restore this backup:
273+
Created at: 2020-03-17 16:28:24 +0000 UTC
274+
Controller: how-bizarre
275+
Juju version: 2.7.5
276+
Models: 3
268277
269-
This controller is in HA and to restore into it successfully,
270-
'juju-restore' needs to manage Juju and Mongo agents on
271-
secondary controller nodes.
272-
However, on the bigger systems, the operator might want to manage
273-
these agents manually.
278+
This controller is in HA and to restore into it successfully, 'juju-restore'
279+
needs to manage Juju and Mongo agents on secondary controller nodes.
280+
However on bigger systems the user might want to manage these agents manually.
274281
275282
Do you want 'juju-restore' to manage these agents automatically? (y/N):
276283
@@ -297,14 +304,15 @@ Checking database and replica set health...
297304
Replica set is healthy ✓
298305
Running on primary HA node ✓
299306
300-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
301-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
307+
You are about to restore this backup:
308+
Created at: 2020-03-17 16:28:24 +0000 UTC
309+
Controller: how-bizarre
310+
Juju version: 2.7.5
311+
Models: 3
302312
303-
This controller is in HA and to restore into it successfully,
304-
'juju-restore' needs to manage Juju and Mongo agents on
305-
secondary controller nodes.
306-
However, on the bigger systems, the operator might want to manage
307-
these agents manually.
313+
This controller is in HA and to restore into it successfully, 'juju-restore'
314+
needs to manage Juju and Mongo agents on secondary controller nodes.
315+
However on bigger systems the user might want to manage these agents manually.
308316
309317
Do you want 'juju-restore' to manage these agents automatically? (y/N):
310318
@@ -333,14 +341,15 @@ Checking database and replica set health...
333341
Replica set is healthy ✓
334342
Running on primary HA node ✓
335343
336-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
337-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
344+
You are about to restore this backup:
345+
Created at: 2020-03-17 16:28:24 +0000 UTC
346+
Controller: how-bizarre
347+
Juju version: 2.7.5
348+
Models: 3
338349
339-
This controller is in HA and to restore into it successfully,
340-
'juju-restore' needs to manage Juju and Mongo agents on
341-
secondary controller nodes.
342-
However, on the bigger systems, the operator might want to manage
343-
these agents manually.
350+
This controller is in HA and to restore into it successfully, 'juju-restore'
351+
needs to manage Juju and Mongo agents on secondary controller nodes.
352+
However on bigger systems the user might want to manage these agents manually.
344353
345354
Do you want 'juju-restore' to manage these agents automatically? (y/N):
346355
All restore pre-checks are completed.
@@ -367,12 +376,15 @@ Checking database and replica set health...
367376
Replica set is healthy ✓
368377
Running on primary HA node ✓
369378
370-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
371-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
379+
You are about to restore this backup:
380+
Created at: 2020-03-17 16:28:24 +0000 UTC
381+
Controller: how-bizarre
382+
Juju version: 2.7.5
383+
Models: 3
372384
373385
Juju agents on secondary controller machines must be stopped by this point.
374386
To stop the agents, login into each secondary controller and run:
375-
$ systemctl stop jujud-machine-*
387+
$ sudo systemctl stop jujud-machine-*
376388
377389
All restore pre-checks are completed.
378390
@@ -412,12 +424,15 @@ Checking database and replica set health...
412424
Replica set is healthy ✓
413425
Running on primary HA node ✓
414426
415-
You are about to restore a controller from a backup file taken on 2020-03-17 16:28:24 +0000 UTC.
416-
It contains a controller how-bizarre at Juju version 2.7.5 with 3 models.
427+
You are about to restore this backup:
428+
Created at: 2020-03-17 16:28:24 +0000 UTC
429+
Controller: how-bizarre
430+
Juju version: 2.7.5
431+
Models: 3
417432
418433
Juju agents on secondary controller machines must be stopped by this point.
419434
To stop the agents, login into each secondary controller and run:
420-
$ systemctl stop jujud-machine-*
435+
$ sudo systemctl stop jujud-machine-*
421436
422437
All restore pre-checks are completed.
423438
@@ -478,32 +493,53 @@ func (s *restoreSuite) TestLoadsCredsIfNoUsername(c *gc.C) {
478493
c.Assert(err, gc.ErrorMatches, "loading credentials: loading those creds")
479494
}
480495

496+
type readerFunc func(string) ([]byte, error)
497+
498+
func makeFakeReader(c *gc.C, expectedPath string, contents []byte) readerFunc {
499+
return func(path string) ([]byte, error) {
500+
c.Assert(path, gc.Equals, expectedPath)
501+
return contents, nil
502+
}
503+
}
504+
481505
func (s *restoreSuite) TestReadCredsFromPattern(c *gc.C) {
482506
dir := c.MkDir()
483-
err := ioutil.WriteFile(filepath.Join(dir, "agent.conf"), []byte(agentConfContents), 0777)
507+
confPath := filepath.Join(dir, "agent.conf")
508+
err := ioutil.WriteFile(confPath, nil, 0777)
484509
c.Assert(err, jc.ErrorIsNil)
485510

486-
username, password, err := cmd.ReadCredsFromPattern(filepath.Join(dir, "*.conf"))
511+
username, password, err := cmd.ReadCredsFromPattern(
512+
filepath.Join(dir, "*.conf"),
513+
makeFakeReader(c, confPath, []byte(agentConfContents)),
514+
)
487515
c.Assert(err, jc.ErrorIsNil)
488516
c.Assert(username, gc.Equals, "porridge-radio")
489517
c.Assert(password, gc.Equals, "lilac")
490518
}
491519

492520
func (s *restoreSuite) TestReadCredsMissingUsername(c *gc.C) {
493521
dir := c.MkDir()
494-
err := ioutil.WriteFile(filepath.Join(dir, "agent.conf"), []byte(missingTagConf), 0777)
522+
confPath := filepath.Join(dir, "agent.conf")
523+
err := ioutil.WriteFile(confPath, nil, 0777)
495524
c.Assert(err, jc.ErrorIsNil)
496525

497-
_, _, err = cmd.ReadCredsFromPattern(filepath.Join(dir, "*.conf"))
526+
_, _, err = cmd.ReadCredsFromPattern(
527+
filepath.Join(dir, "*.conf"),
528+
makeFakeReader(c, confPath, []byte(missingTagConf)),
529+
)
498530
c.Assert(err, gc.ErrorMatches, `no username found in ".*/agent\.conf" - tag field is missing or blank`)
499531
}
500532

501533
func (s *restoreSuite) TestReadCredsMissingPassword(c *gc.C) {
502534
dir := c.MkDir()
503-
err := ioutil.WriteFile(filepath.Join(dir, "agent.conf"), []byte(missingPasswordConf), 0777)
535+
confPath := filepath.Join(dir, "agent.conf")
536+
err := ioutil.WriteFile(confPath, nil, 0777)
504537
c.Assert(err, jc.ErrorIsNil)
505538

506-
_, _, err = cmd.ReadCredsFromPattern(filepath.Join(dir, "*.conf"))
539+
_, _, err = cmd.ReadCredsFromPattern(
540+
filepath.Join(dir, "*.conf"),
541+
makeFakeReader(c, confPath, []byte(missingPasswordConf)),
542+
)
507543
c.Assert(err, gc.ErrorMatches, `no password found in ".*/agent\.conf" - statepassword field is missing or blank`)
508544
}
509545

0 commit comments

Comments
 (0)