github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/plugins/juju-metadata/toolsmetadata_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"regexp"
    12  	"runtime"
    13  	"sort"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"github.com/juju/cmd"
    18  	"github.com/juju/cmd/cmdtesting"
    19  	"github.com/juju/loggo"
    20  	jc "github.com/juju/testing/checkers"
    21  	gc "gopkg.in/check.v1"
    22  
    23  	"github.com/juju/juju/cmd/modelcmd"
    24  	"github.com/juju/juju/environs"
    25  	"github.com/juju/juju/environs/bootstrap"
    26  	"github.com/juju/juju/environs/config"
    27  	"github.com/juju/juju/environs/tools"
    28  	toolstesting "github.com/juju/juju/environs/tools/testing"
    29  	"github.com/juju/juju/juju/keys"
    30  	"github.com/juju/juju/juju/osenv"
    31  	"github.com/juju/juju/jujuclient"
    32  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    33  	"github.com/juju/juju/provider/dummy"
    34  	coretesting "github.com/juju/juju/testing"
    35  	jujuversion "github.com/juju/juju/version"
    36  )
    37  
    38  type ToolsMetadataSuite struct {
    39  	coretesting.FakeJujuXDGDataHomeSuite
    40  	env              environs.Environ
    41  	publicStorageDir string
    42  }
    43  
    44  var _ = gc.Suite(&ToolsMetadataSuite{})
    45  
    46  func (s *ToolsMetadataSuite) SetUpTest(c *gc.C) {
    47  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
    48  	s.AddCleanup(dummy.Reset)
    49  	cfg, err := config.New(config.UseDefaults, map[string]interface{}{
    50  		"name":            "erewhemos",
    51  		"type":            "dummy",
    52  		"uuid":            coretesting.ModelTag.Id(),
    53  		"controller-uuid": coretesting.ControllerTag.Id(),
    54  		"conroller":       true,
    55  	})
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	e, err := bootstrap.PrepareController(
    58  		false,
    59  		modelcmd.BootstrapContextNoVerify(cmdtesting.Context(c)),
    60  		jujuclient.NewMemStore(),
    61  		bootstrap.PrepareParams{
    62  			ControllerConfig: coretesting.FakeControllerConfig(),
    63  			ControllerName:   cfg.Name(),
    64  			ModelConfig:      cfg.AllAttrs(),
    65  			Cloud:            dummy.SampleCloudSpec(),
    66  			AdminSecret:      "admin-secret",
    67  		},
    68  	)
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	s.env = e.(environs.Environ)
    71  	loggo.GetLogger("").SetLogLevel(loggo.INFO)
    72  
    73  	// Switch the default tools location.
    74  	s.publicStorageDir = c.MkDir()
    75  	s.PatchValue(&tools.DefaultBaseURL, s.publicStorageDir)
    76  }
    77  
    78  var currentVersionStrings = []string{
    79  	// only these ones will make it into the JSON files.
    80  	jujuversion.Current.String() + "-quantal-amd64",
    81  	jujuversion.Current.String() + "-quantal-armhf",
    82  	jujuversion.Current.String() + "-quantal-i386",
    83  }
    84  
    85  var versionStrings = append([]string{
    86  	fmt.Sprintf("%d.12.0-precise-amd64", jujuversion.Current.Major),
    87  	fmt.Sprintf("%d.12.0-precise-i386", jujuversion.Current.Major),
    88  	fmt.Sprintf("%d.12.0-raring-amd64", jujuversion.Current.Major),
    89  	fmt.Sprintf("%d.12.0-raring-i386", jujuversion.Current.Major),
    90  	fmt.Sprintf("%d.13.0-precise-amd64", jujuversion.Current.Major+1),
    91  }, currentVersionStrings...)
    92  
    93  var expectedOutputCommon = makeExpectedOutputCommon()
    94  
    95  func makeExpectedOutputCommon() string {
    96  	expected := "Finding agent binaries in .*\n"
    97  	f := `.*Fetching agent binaries from dir "{{.ToolsDir}}" to generate hash: %s` + "\n"
    98  
    99  	// Sort the global versionStrings
   100  	sort.Strings(versionStrings)
   101  	for _, v := range versionStrings {
   102  		expected += fmt.Sprintf(f, regexp.QuoteMeta(v))
   103  	}
   104  	return strings.TrimSpace(expected)
   105  }
   106  
   107  func makeExpectedOutput(templ, stream, toolsDir string) string {
   108  	t := template.Must(template.New("").Parse(templ))
   109  
   110  	var buf bytes.Buffer
   111  	err := t.Execute(&buf, map[string]interface{}{"Stream": stream, "ToolsDir": toolsDir})
   112  	if err != nil {
   113  		panic(err)
   114  	}
   115  	return buf.String()
   116  }
   117  
   118  var expectedOutputDirectoryTemplate = expectedOutputCommon + `
   119  .*Writing tools/streams/v1/index2\.json
   120  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   121  `
   122  
   123  func newToolsMetadataCommandForTest() cmd.Command {
   124  	cmd := &toolsMetadataCommand{}
   125  	cmd.SetClientStore(jujuclienttesting.MinimalStore())
   126  	return modelcmd.Wrap(cmd)
   127  }
   128  
   129  func (s *ToolsMetadataSuite) TestGenerateToDirectory(c *gc.C) {
   130  	metadataDir := c.MkDir()
   131  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   132  	ctx := cmdtesting.Context(c)
   133  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir})
   134  	c.Check(code, gc.Equals, 0)
   135  	output := ctx.Stdout.(*bytes.Buffer).String()
   136  
   137  	outputDirReleasedTmpl := expectedOutputCommon + `
   138  .*Writing tools/streams/v1/index2\.json
   139  .*Writing tools/streams/v1/index\.json
   140  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   141  `
   142  	expectedOutput := makeExpectedOutput(outputDirReleasedTmpl, "released", "released")
   143  	c.Check(output, gc.Matches, expectedOutput)
   144  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   145  	c.Check(metadata, gc.HasLen, len(versionStrings))
   146  	obtainedVersionStrings := make([]string, len(versionStrings))
   147  	for i, metadata := range metadata {
   148  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   149  		obtainedVersionStrings[i] = s
   150  	}
   151  	c.Check(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   152  }
   153  
   154  func (s *ToolsMetadataSuite) TestGenerateStream(c *gc.C) {
   155  	metadataDir := c.MkDir()
   156  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   157  	ctx := cmdtesting.Context(c)
   158  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   159  	c.Assert(code, gc.Equals, 0)
   160  	output := ctx.Stdout.(*bytes.Buffer).String()
   161  	c.Assert(output, gc.Matches, makeExpectedOutput(expectedOutputDirectoryTemplate, "proposed", "proposed"))
   162  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   163  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   164  	obtainedVersionStrings := make([]string, len(versionStrings))
   165  	for i, metadata := range metadata {
   166  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   167  		obtainedVersionStrings[i] = s
   168  	}
   169  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   170  }
   171  
   172  func (s *ToolsMetadataSuite) TestGenerateMultipleStreams(c *gc.C) {
   173  	metadataDir := c.MkDir()
   174  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   175  	toolstesting.MakeTools(c, metadataDir, "released", currentVersionStrings)
   176  
   177  	ctx := cmdtesting.Context(c)
   178  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   179  	c.Assert(code, gc.Equals, 0)
   180  	code = cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   181  	c.Assert(code, gc.Equals, 0)
   182  
   183  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   184  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   185  	obtainedVersionStrings := make([]string, len(versionStrings))
   186  	for i, metadata := range metadata {
   187  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   188  		obtainedVersionStrings[i] = s
   189  	}
   190  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   191  
   192  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   193  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   194  	obtainedVersionStrings = make([]string, len(currentVersionStrings))
   195  	for i, metadata := range metadata {
   196  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   197  		obtainedVersionStrings[i] = s
   198  	}
   199  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   200  
   201  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   202  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   203  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   204  	obtainedVersionStrings = make([]string, len(versionStrings))
   205  	for i, metadata := range metadata {
   206  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   207  		obtainedVersionStrings[i] = s
   208  	}
   209  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   210  }
   211  
   212  func (s *ToolsMetadataSuite) TestGenerateDeleteExisting(c *gc.C) {
   213  	metadataDir := c.MkDir()
   214  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   215  	toolstesting.MakeTools(c, metadataDir, "released", currentVersionStrings)
   216  
   217  	ctx := cmdtesting.Context(c)
   218  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   219  	c.Assert(code, gc.Equals, 0)
   220  	code = cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   221  	c.Assert(code, gc.Equals, 0)
   222  
   223  	// Remove existing proposed tarballs, and create some different ones.
   224  	err := os.RemoveAll(filepath.Join(metadataDir, "tools", "proposed"))
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	toolstesting.MakeTools(c, metadataDir, "proposed", currentVersionStrings)
   227  
   228  	// Generate proposed metadata again, using --clean.
   229  	code = cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "proposed", "--clean"})
   230  	c.Assert(code, gc.Equals, 0)
   231  
   232  	// Proposed metadata should just list the tarballs that were there, not the merged set.
   233  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   234  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   235  	obtainedVersionStrings := make([]string, len(currentVersionStrings))
   236  	for i, metadata := range metadata {
   237  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   238  		obtainedVersionStrings[i] = s
   239  	}
   240  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   241  
   242  	// Released metadata should be untouched.
   243  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   244  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   245  	obtainedVersionStrings = make([]string, len(currentVersionStrings))
   246  	for i, metadata := range metadata {
   247  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   248  		obtainedVersionStrings[i] = s
   249  	}
   250  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   251  }
   252  
   253  func (s *ToolsMetadataSuite) TestGenerateWithPublicFallback(c *gc.C) {
   254  	// Write tools and metadata to the public tools location.
   255  	toolstesting.MakeToolsWithCheckSum(c, s.publicStorageDir, "released", versionStrings)
   256  
   257  	// Run the command with no local metadata.
   258  	ctx := cmdtesting.Context(c)
   259  	metadataDir := c.MkDir()
   260  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   261  	c.Assert(code, gc.Equals, 0)
   262  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   263  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   264  	obtainedVersionStrings := make([]string, len(versionStrings))
   265  	for i, metadata := range metadata {
   266  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   267  		obtainedVersionStrings[i] = s
   268  	}
   269  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   270  }
   271  
   272  func (s *ToolsMetadataSuite) TestGenerateWithMirrors(c *gc.C) {
   273  
   274  	metadataDir := c.MkDir()
   275  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   276  	ctx := cmdtesting.Context(c)
   277  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"--public", "-d", metadataDir, "--stream", "released"})
   278  	c.Assert(code, gc.Equals, 0)
   279  	output := ctx.Stdout.(*bytes.Buffer).String()
   280  
   281  	mirrosTmpl := expectedOutputCommon + `
   282  .*Writing tools/streams/v1/index2\.json
   283  .*Writing tools/streams/v1/index\.json
   284  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   285  .*Writing tools/streams/v1/mirrors\.json
   286  `
   287  	expectedOutput := makeExpectedOutput(mirrosTmpl, "released", "released")
   288  	c.Check(output, gc.Matches, expectedOutput)
   289  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", true)
   290  	c.Check(metadata, gc.HasLen, len(versionStrings))
   291  	obtainedVersionStrings := make([]string, len(versionStrings))
   292  	for i, metadata := range metadata {
   293  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   294  		obtainedVersionStrings[i] = s
   295  	}
   296  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   297  }
   298  
   299  func (s *ToolsMetadataSuite) TestNoTools(c *gc.C) {
   300  	if runtime.GOOS == "windows" {
   301  		c.Skip("Skipping on windows, test only set up for Linux tools")
   302  	}
   303  	ctx := cmdtesting.Context(c)
   304  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, nil)
   305  	c.Assert(code, gc.Equals, 1)
   306  	stdout := ctx.Stdout.(*bytes.Buffer).String()
   307  	c.Assert(stdout, gc.Matches, ".*Finding agent binaries in .*\n")
   308  	stderr := ctx.Stderr.(*bytes.Buffer).String()
   309  	c.Assert(stderr, gc.Matches, "ERROR no agent binaries available\n")
   310  }
   311  
   312  func (s *ToolsMetadataSuite) TestPatchLevels(c *gc.C) {
   313  	if runtime.GOOS == "windows" {
   314  		c.Skip("Skipping on windows, test only set up for Linux tools")
   315  	}
   316  	currentVersion := jujuversion.Current
   317  	currentVersion.Build = 0
   318  	versionStrings := []string{
   319  		currentVersion.String() + "-precise-amd64",
   320  		currentVersion.String() + ".1-precise-amd64",
   321  	}
   322  	metadataDir := osenv.JujuXDGDataHomeDir() // default metadata dir
   323  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   324  	ctx := cmdtesting.Context(c)
   325  	code := cmd.Main(newToolsMetadataCommandForTest(), ctx, []string{"--stream", "released"})
   326  	c.Assert(code, gc.Equals, 0)
   327  	output := ctx.Stdout.(*bytes.Buffer).String()
   328  	expectedOutput := fmt.Sprintf(`
   329  Finding agent binaries in .*
   330  .*Fetching agent binaries from dir "released" to generate hash: %s
   331  .*Fetching agent binaries from dir "released" to generate hash: %s
   332  .*Writing tools/streams/v1/index2\.json
   333  .*Writing tools/streams/v1/index\.json
   334  .*Writing tools/streams/v1/com\.ubuntu\.juju-released-tools\.json
   335  `[1:], regexp.QuoteMeta(versionStrings[0]), regexp.QuoteMeta(versionStrings[1]))
   336  	c.Assert(output, gc.Matches, expectedOutput)
   337  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   338  	c.Assert(metadata, gc.HasLen, 2)
   339  
   340  	filename := fmt.Sprintf("juju-%s-precise-amd64.tgz", currentVersion)
   341  	size, sha256 := toolstesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "released", filename))
   342  	c.Assert(metadata[0], gc.DeepEquals, &tools.ToolsMetadata{
   343  		Release:  "precise",
   344  		Version:  currentVersion.String(),
   345  		Arch:     "amd64",
   346  		Size:     size,
   347  		Path:     "released/" + filename,
   348  		FileType: "tar.gz",
   349  		SHA256:   sha256,
   350  	})
   351  
   352  	filename = fmt.Sprintf("juju-%s.1-precise-amd64.tgz", currentVersion)
   353  	size, sha256 = toolstesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "released", filename))
   354  	c.Assert(metadata[1], gc.DeepEquals, &tools.ToolsMetadata{
   355  		Release:  "precise",
   356  		Version:  currentVersion.String() + ".1",
   357  		Arch:     "amd64",
   358  		Size:     size,
   359  		Path:     "released/" + filename,
   360  		FileType: "tar.gz",
   361  		SHA256:   sha256,
   362  	})
   363  }
   364  
   365  func (s *ToolsMetadataSuite) TestToolsDataSourceHasKey(c *gc.C) {
   366  	ds := toolsDataSources("test.me")
   367  	// This data source does not require to contain signed data.
   368  	// However, it may still contain it.
   369  	// Since we will always try to read signed data first,
   370  	// we want to be able to try to read this signed data
   371  	// with public key with Juju-known public key for tools.
   372  	// Bugs #1542127, #1542131
   373  	c.Assert(ds[0].PublicSigningKey(), gc.DeepEquals, keys.JujuPublicKey)
   374  }