diff --git a/cloudconfig/cloudinit/cloudinit_test.go b/cloudconfig/cloudinit/cloudinit_test.go index 710f8f8..d5f6b6c 100644 --- a/cloudconfig/cloudinit/cloudinit_test.go +++ b/cloudconfig/cloudinit/cloudinit_test.go @@ -210,6 +210,44 @@ var ctests = []struct { cfg.AddPackage("ubuntu") }, }, { + "Packages on precise, needing cloud-tools", + map[string]interface{}{"packages": []string{ + // Regular packages (not needing the cloud-tools archive) + "juju", + "curl", + // The following need to be among the list of cloud-tools + // packages (see cloudArchivePackagesUbuntu in juju/utils + // repo). + "--target-release", "precise-updates/cloud-tools", "cloud-utils", + "--target-release", "precise-updates/cloud-tools", "cloud-image-utils", + // Other regular packages. + "ubuntu", + }}, + func(cfg cloudinit.CloudConfig) { + cfg.AddPackage("juju") + cfg.AddPackage("curl") + // cloud-tools packages need to appear in the list of packages + // after the "--target-release" and + // "precise-updates/cloud-tools" lines to work around the + // broken 0.6.3 cloud-init on precise. cloud-init 0.6.3 + // concatenates all space-separated arguments for each entry + // in the packages list, and then escapes the result in single + // quotes, which in turn leads to incorrectly rendered apt-get + // command (e.g. instead of "apt-get install --target-release + // foo/bar package" 0.6.3 will try to execute "apt-get install + // '--target-release foo/bar package'".). See bug #1424777 for + // more info. + cfg.AddPackage("--target-release") + cfg.AddPackage("precise-updates/cloud-tools") + cfg.AddPackage("cloud-utils") + + cfg.AddPackage("--target-release") + cfg.AddPackage("precise-updates/cloud-tools") + cfg.AddPackage("cloud-image-utils") + + cfg.AddPackage("ubuntu") + }, +}, { "BootCmd", map[string]interface{}{"bootcmd": []string{ "ls > /dev", @@ -300,7 +338,7 @@ var ctests = []struct { func (S) TestOutput(c *gc.C) { for i, t := range ctests { c.Logf("test %d: %s", i, t.name) - cfg, err := cloudinit.New("trusty") + cfg, err := cloudinit.New("precise") c.Assert(err, jc.ErrorIsNil) t.setOption(cfg) data, err := cfg.RenderYAML() @@ -315,7 +353,7 @@ func (S) TestOutput(c *gc.C) { } func (S) TestRunCmds(c *gc.C) { - cfg, err := cloudinit.New("trusty") + cfg, err := cloudinit.New("precise") c.Assert(err, jc.ErrorIsNil) c.Assert(cfg.RunCmds(), gc.HasLen, 0) cfg.AddScripts("a", "b") @@ -326,7 +364,7 @@ func (S) TestRunCmds(c *gc.C) { } func (S) TestPackages(c *gc.C) { - cfg, err := cloudinit.New("trusty") + cfg, err := cloudinit.New("precise") c.Assert(err, jc.ErrorIsNil) c.Assert(cfg.Packages(), gc.HasLen, 0) cfg.AddPackage("a b c") diff --git a/cloudconfig/cloudinit/cloudinit_ubuntu.go b/cloudconfig/cloudinit/cloudinit_ubuntu.go index bfa10ef..a040445 100644 --- a/cloudconfig/cloudinit/cloudinit_ubuntu.go +++ b/cloudconfig/cloudinit/cloudinit_ubuntu.go @@ -196,16 +196,35 @@ func (cfg *ubuntuCloudConfig) getCommandsForAddingPackages() ([]string, error) { cmds = append(cmds, looper+cfg.paccmder.UpgradeCmd()) } + var pkgsWithTargetRelease []string pkgs := cfg.Packages() for i, _ := range pkgs { pack := pkgs[i] - // apply --target-release, if required. - if config.SeriesRequiresCloudArchiveTools(cfg.series) && cfg.pacconfer.IsCloudArchivePackage(pack) { - pack = strings.Join(cfg.pacconfer.ApplyCloudArchiveTarget(pack), " ") + if pack == "--target-release" || len(pkgsWithTargetRelease) > 0 { + // We have --target-release foo/bar package. Accumulate + // the args until we've reached the package, before + // passing the 3 element slice to InstallCmd below. + pkgsWithTargetRelease = append(pkgsWithTargetRelease, pack) + if len(pkgsWithTargetRelease) < 3 { + // We expect exactly 3 elements, the last one being + // the package. + continue + } + } + packageName := pack + installArgs := []string{pack} + + if len(pkgsWithTargetRelease) == 3 { + // If we have a --target-release package, build the + // install command args from the accumulated + // pkgsWithTargetRelease slice and reset it. + installArgs = append([]string{}, pkgsWithTargetRelease...) + packageName = strings.Join(installArgs, " ") + pkgsWithTargetRelease = []string{} } - cmds = append(cmds, LogProgressCmd("Installing package: %s", pkgs[i])) - cmd := looper + cfg.paccmder.InstallCmd(pack) + cmds = append(cmds, LogProgressCmd("Installing package: %s", packageName)) + cmd := looper + cfg.paccmder.InstallCmd(installArgs...) cmds = append(cmds, cmd) } @@ -263,9 +282,15 @@ func (cfg *ubuntuCloudConfig) addRequiredPackages() { for _, pack := range packages { if config.SeriesRequiresCloudArchiveTools(cfg.series) && cfg.pacconfer.IsCloudArchivePackage(pack) { // On precise, we need to pass a --target-release entry in - // pieces for it to work: - // TODO (aznashwan): figure out what the hell precise wants. - cfg.AddPackage(strings.Join(cfg.pacconfer.ApplyCloudArchiveTarget(pack), " ")) + // pieces (as "packages") for it to work: + // --target-release, precise-updates/cloud-tools, + // package-name. All these 3 entries are needed so + // cloud-init 0.6.3 can generate the correct apt-get + // install command line for "package-name". + args := cfg.pacconfer.ApplyCloudArchiveTarget(pack) + for _, arg := range args { + cfg.AddPackage(arg) + } } else { cfg.AddPackage(pack) } diff --git a/container/lxc/initialisation_test.go b/container/lxc/initialisation_test.go index 8fe96c1..d5a673c 100644 --- a/container/lxc/initialisation_test.go +++ b/container/lxc/initialisation_test.go @@ -50,8 +50,8 @@ func (s *InitialiserSuite) TestLTSSeriesPackages(c *gc.C) { c.Assert(err, jc.ErrorIsNil) c.Assert(s.calledCmds, gc.DeepEquals, []string{ - paccmder.InstallCmd("--target-release precise-updates/cloud-tools lxc"), - paccmder.InstallCmd("--target-release precise-updates/cloud-tools cloud-image-utils"), + paccmder.InstallCmd("--target-release", "precise-updates/cloud-tools", "lxc"), + paccmder.InstallCmd("--target-release", "precise-updates/cloud-tools", "cloud-image-utils"), }) }