github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/charmhub/refresh_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package charmhub
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"io"
    10  	"net/http"
    11  
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils/v3"
    17  	"go.uber.org/mock/gomock"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/charmhub/path"
    21  	"github.com/juju/juju/charmhub/transport"
    22  	"github.com/juju/juju/core/arch"
    23  	charmmetrics "github.com/juju/juju/core/charm/metrics"
    24  )
    25  
    26  type RefreshSuite struct {
    27  	testing.IsolationSuite
    28  }
    29  
    30  var (
    31  	_ = gc.Suite(&RefreshSuite{})
    32  
    33  	expRefreshFields = set.NewStrings(
    34  		"download", "id", "license", "name", "publisher", "resources",
    35  		"revision", "summary", "type", "version", "bases", "config-yaml",
    36  		"metadata-yaml",
    37  	).SortedValues()
    38  )
    39  
    40  func (s *RefreshSuite) TestRefresh(c *gc.C) {
    41  	ctrl := gomock.NewController(c)
    42  	defer ctrl.Finish()
    43  
    44  	baseURL := MustParseURL(c, "http://api.foo.bar")
    45  
    46  	baseURLPath := path.MakePath(baseURL)
    47  	id := "meshuggah"
    48  	body := transport.RefreshRequest{
    49  		Context: []transport.RefreshRequestContext{{
    50  			InstanceKey: "instance-key",
    51  			ID:          id,
    52  			Revision:    1,
    53  			Base: transport.Base{
    54  				Name:         "ubuntu",
    55  				Channel:      "20.04",
    56  				Architecture: arch.DefaultArchitecture,
    57  			},
    58  			TrackingChannel: "latest/stable",
    59  		}},
    60  		Actions: []transport.RefreshRequestAction{{
    61  			Action:      "refresh",
    62  			InstanceKey: "instance-key",
    63  			ID:          &id,
    64  		}},
    65  		Fields: expRefreshFields,
    66  	}
    67  
    68  	config, err := RefreshOne("instance-key", id, 1, "latest/stable", RefreshBase{
    69  		Name:         "ubuntu",
    70  		Channel:      "20.04",
    71  		Architecture: arch.DefaultArchitecture,
    72  	})
    73  	c.Assert(err, jc.ErrorIsNil)
    74  
    75  	restClient := NewMockRESTClient(ctrl)
    76  	s.expectPost(restClient, baseURLPath, id, body)
    77  
    78  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
    79  	responses, err := client.Refresh(context.Background(), config)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  	c.Assert(len(responses), gc.Equals, 1)
    82  	c.Assert(responses[0].Name, gc.Equals, id)
    83  }
    84  
    85  // c.Assert(results.Results[0].Error, gc.ErrorMatches, `.* pool "foo" not found`)
    86  func (s *RefreshSuite) TestRefeshConfigValidateArch(c *gc.C) {
    87  	err := s.testRefeshConfigValidate(c, RefreshBase{
    88  		Name:         "ubuntu",
    89  		Channel:      "20.04",
    90  		Architecture: "all",
    91  	})
    92  	c.Assert(err, gc.ErrorMatches, "Architecture.*")
    93  }
    94  
    95  func (s *RefreshSuite) TestRefeshConfigValidateSeries(c *gc.C) {
    96  	err := s.testRefeshConfigValidate(c, RefreshBase{
    97  		Name:         "ubuntu",
    98  		Channel:      "all",
    99  		Architecture: arch.DefaultArchitecture,
   100  	})
   101  	c.Assert(err, gc.ErrorMatches, "Channel.*")
   102  }
   103  
   104  func (s *RefreshSuite) TestRefeshConfigVali914dateName(c *gc.C) {
   105  	err := s.testRefeshConfigValidate(c, RefreshBase{
   106  		Name:         "all",
   107  		Channel:      "20.04",
   108  		Architecture: arch.DefaultArchitecture,
   109  	})
   110  	c.Assert(err, gc.ErrorMatches, "Name.*")
   111  }
   112  
   113  func (s *RefreshSuite) TestRefeshConfigValidate(c *gc.C) {
   114  	err := s.testRefeshConfigValidate(c, RefreshBase{
   115  		Name:         "all",
   116  		Channel:      "all",
   117  		Architecture: "all",
   118  	})
   119  	c.Assert(err, gc.ErrorMatches, "Architecture.*, Name.*, Channel.*")
   120  }
   121  
   122  func (s *RefreshSuite) testRefeshConfigValidate(c *gc.C, rp RefreshBase) error {
   123  	_, err := DownloadOneFromChannel("meshuggah", "latest/stable", rp)
   124  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   125  	return err
   126  }
   127  
   128  type metadataHTTPClient struct {
   129  	requestHeaders http.Header
   130  	responseBody   string
   131  }
   132  
   133  func (t *metadataHTTPClient) Do(req *http.Request) (*http.Response, error) {
   134  	t.requestHeaders = req.Header
   135  	resp := &http.Response{
   136  		Status:        "200 OK",
   137  		StatusCode:    200,
   138  		Proto:         "HTTP/1.1",
   139  		ProtoMajor:    1,
   140  		ProtoMinor:    1,
   141  		Body:          io.NopCloser(bytes.NewBufferString(t.responseBody)),
   142  		ContentLength: int64(len(t.responseBody)),
   143  		Request:       req,
   144  		Header:        http.Header{"Content-Type": []string{"application/json"}},
   145  	}
   146  	return resp, nil
   147  }
   148  
   149  func (s *RefreshSuite) TestRefreshMetadata(c *gc.C) {
   150  	baseURL := MustParseURL(c, "http://api.foo.bar")
   151  	baseURLPath := path.MakePath(baseURL)
   152  
   153  	httpClient := &metadataHTTPClient{
   154  		responseBody: `
   155  {
   156    "error-list": [],
   157    "results": [
   158      {
   159        "id": "foo",
   160        "instance-key": "instance-key-foo"
   161      },
   162      {
   163        "id": "bar",
   164        "instance-key": "instance-key-bar"
   165      }
   166    ]
   167  }
   168  `,
   169  	}
   170  
   171  	restClient := newHTTPRESTClient(httpClient)
   172  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
   173  
   174  	config1, err := RefreshOne("instance-key-foo", "foo", 1, "latest/stable", RefreshBase{
   175  		Name:         "ubuntu",
   176  		Channel:      "20.04",
   177  		Architecture: "amd64",
   178  	})
   179  	c.Assert(err, jc.ErrorIsNil)
   180  
   181  	config2, err := RefreshOne("instance-key-bar", "bar", 2, "latest/edge", RefreshBase{
   182  		Name:         "ubuntu",
   183  		Channel:      "14.04",
   184  		Architecture: "amd64",
   185  	})
   186  	c.Assert(err, jc.ErrorIsNil)
   187  
   188  	config := RefreshMany(config1, config2)
   189  
   190  	response, err := client.Refresh(context.Background(), config)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	c.Assert(response, gc.DeepEquals, []transport.RefreshResponse{
   194  		{ID: "foo", InstanceKey: "instance-key-foo"},
   195  		{ID: "bar", InstanceKey: "instance-key-bar"},
   196  	})
   197  }
   198  
   199  func (s *RefreshSuite) TestRefreshMetadataRandomOrder(c *gc.C) {
   200  	baseURL := MustParseURL(c, "http://api.foo.bar")
   201  	baseURLPath := path.MakePath(baseURL)
   202  
   203  	httpClient := &metadataHTTPClient{
   204  		responseBody: `
   205  {
   206    "error-list": [],
   207    "results": [
   208      {
   209          "id": "bar",
   210          "instance-key": "instance-key-bar"
   211      },
   212      {
   213        "id": "foo",
   214        "instance-key": "instance-key-foo"
   215      }
   216    ]
   217  }
   218  `,
   219  	}
   220  
   221  	restClient := newHTTPRESTClient(httpClient)
   222  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
   223  
   224  	config1, err := RefreshOne("instance-key-foo", "foo", 1, "latest/stable", RefreshBase{
   225  		Name:         "ubuntu",
   226  		Channel:      "20.04",
   227  		Architecture: "amd64",
   228  	})
   229  	c.Assert(err, jc.ErrorIsNil)
   230  
   231  	config2, err := RefreshOne("instance-key-bar", "bar", 2, "latest/edge", RefreshBase{
   232  		Name:         "ubuntu",
   233  		Channel:      "14.04",
   234  		Architecture: "amd64",
   235  	})
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	config := RefreshMany(config1, config2)
   239  
   240  	response, err := client.Refresh(context.Background(), config)
   241  	c.Assert(err, jc.ErrorIsNil)
   242  
   243  	c.Assert(response, gc.DeepEquals, []transport.RefreshResponse{
   244  		{ID: "foo", InstanceKey: "instance-key-foo"},
   245  		{ID: "bar", InstanceKey: "instance-key-bar"},
   246  	})
   247  }
   248  
   249  func (s *RefreshSuite) TestRefreshWithMetricsOnly(c *gc.C) {
   250  	ctrl := gomock.NewController(c)
   251  	defer ctrl.Finish()
   252  
   253  	baseURL := MustParseURL(c, "http://api.foo.bar")
   254  
   255  	baseURLPath := path.MakePath(baseURL)
   256  	id := ""
   257  	body := transport.RefreshRequest{
   258  		Context: []transport.RefreshRequestContext{},
   259  		Actions: []transport.RefreshRequestAction{},
   260  		Metrics: map[string]map[string]string{
   261  			"controller": {"uuid": "controller-uuid"},
   262  			"model":      {"units": "3", "controller": "controller-uuid", "uuid": "model-uuid"},
   263  		},
   264  	}
   265  
   266  	restClient := NewMockRESTClient(ctrl)
   267  	s.expectPost(restClient, baseURLPath, id, body)
   268  
   269  	metrics := map[charmmetrics.MetricKey]map[charmmetrics.MetricKey]string{
   270  		charmmetrics.Controller: {
   271  
   272  			charmmetrics.UUID: "controller-uuid",
   273  		},
   274  		charmmetrics.Model: {
   275  			charmmetrics.NumUnits:   "3",
   276  			charmmetrics.Controller: "controller-uuid",
   277  			charmmetrics.UUID:       "model-uuid",
   278  		},
   279  	}
   280  
   281  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
   282  	err := client.RefreshWithMetricsOnly(context.Background(), metrics)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  }
   285  
   286  func (s *RefreshSuite) TestRefreshWithRequestMetrics(c *gc.C) {
   287  	ctrl := gomock.NewController(c)
   288  	defer ctrl.Finish()
   289  
   290  	baseURL := MustParseURL(c, "http://api.foo.bar")
   291  
   292  	baseURLPath := path.MakePath(baseURL)
   293  	id := "meshuggah"
   294  	body := transport.RefreshRequest{
   295  		Context: []transport.RefreshRequestContext{{
   296  			InstanceKey: "instance-key-foo",
   297  			ID:          id,
   298  			Revision:    1,
   299  			Base: transport.Base{
   300  				Name:         "ubuntu",
   301  				Channel:      "20.04",
   302  				Architecture: arch.DefaultArchitecture,
   303  			},
   304  			TrackingChannel: "latest/stable",
   305  		}, {
   306  			InstanceKey: "instance-key-bar",
   307  			ID:          id,
   308  			Revision:    2,
   309  			Base: transport.Base{
   310  				Name:         "ubuntu",
   311  				Channel:      "14.04",
   312  				Architecture: arch.DefaultArchitecture,
   313  			},
   314  			TrackingChannel: "latest/edge",
   315  		}},
   316  		Actions: []transport.RefreshRequestAction{{
   317  			Action:      "refresh",
   318  			InstanceKey: "instance-key-foo",
   319  			ID:          &id,
   320  		}, {
   321  			Action:      "refresh",
   322  			InstanceKey: "instance-key-bar",
   323  			ID:          &id,
   324  		}},
   325  		Fields: expRefreshFields,
   326  		Metrics: map[string]map[string]string{
   327  			"controller": {"uuid": "controller-uuid"},
   328  			"model":      {"units": "3", "controller": "controller-uuid", "uuid": "model-uuid"},
   329  		},
   330  	}
   331  
   332  	config1, err := RefreshOne("instance-key-foo", id, 1, "latest/stable", RefreshBase{
   333  		Name:         "ubuntu",
   334  		Channel:      "20.04",
   335  		Architecture: "amd64",
   336  	})
   337  	c.Assert(err, jc.ErrorIsNil)
   338  
   339  	config2, err := RefreshOne("instance-key-bar", id, 2, "latest/edge", RefreshBase{
   340  		Name:         "ubuntu",
   341  		Channel:      "14.04",
   342  		Architecture: "amd64",
   343  	})
   344  	c.Assert(err, jc.ErrorIsNil)
   345  
   346  	config := RefreshMany(config1, config2)
   347  
   348  	restClient := NewMockRESTClient(ctrl)
   349  	restClient.EXPECT().Post(gomock.Any(), baseURLPath, gomock.Any(), body, gomock.Any()).Do(func(_ context.Context, _ path.Path, _ map[string][]string, _ transport.RefreshRequest, responses *transport.RefreshResponses) {
   350  		responses.Results = []transport.RefreshResponse{{
   351  			InstanceKey: "instance-key-foo",
   352  			Name:        id,
   353  		}, {
   354  			InstanceKey: "instance-key-bar",
   355  			Name:        id,
   356  		}}
   357  	}).Return(restResponse{StatusCode: http.StatusOK}, nil)
   358  
   359  	metrics := map[charmmetrics.MetricKey]map[charmmetrics.MetricKey]string{
   360  		charmmetrics.Controller: {
   361  			charmmetrics.UUID: "controller-uuid",
   362  		},
   363  		charmmetrics.Model: {
   364  			charmmetrics.NumUnits:   "3",
   365  			charmmetrics.Controller: "controller-uuid",
   366  			charmmetrics.UUID:       "model-uuid",
   367  		},
   368  	}
   369  
   370  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
   371  	responses, err := client.RefreshWithRequestMetrics(context.Background(), config, metrics)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	c.Assert(len(responses), gc.Equals, 2)
   374  	c.Assert(responses[0].Name, gc.Equals, id)
   375  }
   376  
   377  func (s *RefreshSuite) TestRefreshFailure(c *gc.C) {
   378  	ctrl := gomock.NewController(c)
   379  	defer ctrl.Finish()
   380  
   381  	baseURL := MustParseURL(c, "http://api.foo.bar")
   382  
   383  	baseURLPath := path.MakePath(baseURL)
   384  	name := "meshuggah"
   385  
   386  	config, err := RefreshOne("instance-key", name, 1, "latest/stable", RefreshBase{
   387  		Name:         "ubuntu",
   388  		Channel:      "20.04",
   389  		Architecture: arch.DefaultArchitecture,
   390  	})
   391  	c.Assert(err, jc.ErrorIsNil)
   392  
   393  	restClient := NewMockRESTClient(ctrl)
   394  	s.expectPostFailure(restClient)
   395  
   396  	client := newRefreshClient(baseURLPath, restClient, &FakeLogger{})
   397  	_, err = client.Refresh(context.Background(), config)
   398  	c.Assert(err, gc.Not(jc.ErrorIsNil))
   399  }
   400  
   401  func (s *RefreshSuite) expectPost(client *MockRESTClient, p path.Path, name string, body interface{}) {
   402  	client.EXPECT().Post(gomock.Any(), p, gomock.Any(), body, gomock.Any()).Do(func(_ context.Context, _ path.Path, _ map[string][]string, _ transport.RefreshRequest, responses *transport.RefreshResponses) {
   403  		responses.Results = []transport.RefreshResponse{{
   404  			InstanceKey: "instance-key",
   405  			Name:        name,
   406  		}}
   407  	}).Return(restResponse{StatusCode: http.StatusOK}, nil)
   408  }
   409  
   410  func (s *RefreshSuite) expectPostFailure(client *MockRESTClient) {
   411  	client.EXPECT().Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(restResponse{StatusCode: http.StatusInternalServerError}, errors.Errorf("boom"))
   412  }
   413  
   414  func DefineInstanceKey(c *gc.C, config RefreshConfig, key string) RefreshConfig {
   415  	switch t := config.(type) {
   416  	case refreshOne:
   417  		t.instanceKey = key
   418  		return t
   419  	case executeOne:
   420  		t.instanceKey = key
   421  		return t
   422  	case executeOneByRevision:
   423  		t.instanceKey = key
   424  		return t
   425  	default:
   426  		c.Fatalf("unexpected config %T", config)
   427  	}
   428  	return nil
   429  }
   430  
   431  type RefreshConfigSuite struct {
   432  	testing.IsolationSuite
   433  }
   434  
   435  var _ = gc.Suite(&RefreshConfigSuite{})
   436  
   437  func (s *RefreshConfigSuite) TestRefreshOneBuild(c *gc.C) {
   438  	id := "foo"
   439  	config, err := RefreshOne("instance-key", id, 1, "latest/stable", RefreshBase{
   440  		Name:         "ubuntu",
   441  		Channel:      "20.04",
   442  		Architecture: arch.DefaultArchitecture,
   443  	})
   444  	c.Assert(err, jc.ErrorIsNil)
   445  
   446  	req, err := config.Build()
   447  	c.Assert(err, jc.ErrorIsNil)
   448  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   449  		Context: []transport.RefreshRequestContext{{
   450  			InstanceKey: "instance-key",
   451  			ID:          "foo",
   452  			Revision:    1,
   453  			Base: transport.Base{
   454  				Name:         "ubuntu",
   455  				Channel:      "20.04",
   456  				Architecture: arch.DefaultArchitecture,
   457  			},
   458  			TrackingChannel: "latest/stable",
   459  		}},
   460  		Actions: []transport.RefreshRequestAction{{
   461  			Action:      "refresh",
   462  			InstanceKey: "instance-key",
   463  			ID:          &id,
   464  		}},
   465  		Fields: expRefreshFields,
   466  	})
   467  }
   468  
   469  func (s *RefreshConfigSuite) TestRefreshOneWithBaseChannelRiskBuild(c *gc.C) {
   470  	id := "foo"
   471  	config, err := RefreshOne("instance-key", id, 1, "latest/stable", RefreshBase{
   472  		Name:         "ubuntu",
   473  		Channel:      "20.04/stable",
   474  		Architecture: arch.DefaultArchitecture,
   475  	})
   476  	c.Assert(err, jc.ErrorIsNil)
   477  
   478  	req, err := config.Build()
   479  	c.Assert(err, jc.ErrorIsNil)
   480  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   481  		Context: []transport.RefreshRequestContext{{
   482  			InstanceKey: "instance-key",
   483  			ID:          "foo",
   484  			Revision:    1,
   485  			Base: transport.Base{
   486  				Name:         "ubuntu",
   487  				Channel:      "20.04",
   488  				Architecture: arch.DefaultArchitecture,
   489  			},
   490  			TrackingChannel: "latest/stable",
   491  		}},
   492  		Actions: []transport.RefreshRequestAction{{
   493  			Action:      "refresh",
   494  			InstanceKey: "instance-key",
   495  			ID:          &id,
   496  		}},
   497  		Fields: expRefreshFields,
   498  	})
   499  
   500  }
   501  
   502  func (s *RefreshConfigSuite) TestRefreshOneBuildInstanceKeyCompatibility(c *gc.C) {
   503  	id := "foo"
   504  	config, err := RefreshOne("", id, 1, "latest/stable", RefreshBase{
   505  		Name:         "ubuntu",
   506  		Channel:      "20.04",
   507  		Architecture: arch.DefaultArchitecture,
   508  	})
   509  	c.Assert(err, jc.ErrorIsNil)
   510  
   511  	key := ExtractConfigInstanceKey(config)
   512  	c.Assert(utils.IsValidUUIDString(key), jc.IsTrue)
   513  }
   514  
   515  func (s *RefreshConfigSuite) TestRefreshOneWithMetricsBuild(c *gc.C) {
   516  	id := "foo"
   517  	config, err := RefreshOne("instance-key", id, 1, "latest/stable", RefreshBase{
   518  		Name:         "ubuntu",
   519  		Channel:      "20.04",
   520  		Architecture: arch.DefaultArchitecture,
   521  	})
   522  	c.Assert(err, jc.ErrorIsNil)
   523  
   524  	config, err = AddConfigMetrics(config, map[charmmetrics.MetricKey]string{
   525  		charmmetrics.Provider:        "openstack",
   526  		charmmetrics.NumApplications: "4",
   527  	})
   528  	c.Assert(err, jc.ErrorIsNil)
   529  
   530  	req, err := config.Build()
   531  	c.Assert(err, jc.ErrorIsNil)
   532  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   533  		Context: []transport.RefreshRequestContext{{
   534  			InstanceKey: "instance-key",
   535  			ID:          "foo",
   536  			Revision:    1,
   537  			Base: transport.Base{
   538  				Name:         "ubuntu",
   539  				Channel:      "20.04",
   540  				Architecture: arch.DefaultArchitecture,
   541  			},
   542  			TrackingChannel: "latest/stable",
   543  			Metrics: map[string]string{
   544  				"provider":     "openstack",
   545  				"applications": "4",
   546  			},
   547  		}},
   548  		Actions: []transport.RefreshRequestAction{{
   549  			Action:      "refresh",
   550  			InstanceKey: "instance-key",
   551  			ID:          &id,
   552  		}},
   553  		Fields: expRefreshFields,
   554  	})
   555  }
   556  
   557  func (s *RefreshConfigSuite) TestRefreshOneFail(c *gc.C) {
   558  	_, err := RefreshOne("instance-key", "", 1, "latest/stable", RefreshBase{
   559  		Name:         "ubuntu",
   560  		Channel:      "20.04",
   561  		Architecture: arch.DefaultArchitecture,
   562  	})
   563  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   564  }
   565  
   566  func (s *RefreshConfigSuite) TestRefreshOneEnsure(c *gc.C) {
   567  	config, err := RefreshOne("instance-key", "foo", 1, "latest/stable", RefreshBase{
   568  		Name:         "ubuntu",
   569  		Channel:      "20.04",
   570  		Architecture: arch.DefaultArchitecture,
   571  	})
   572  	c.Assert(err, jc.ErrorIsNil)
   573  
   574  	err = config.Ensure([]transport.RefreshResponse{{
   575  		InstanceKey: "instance-key",
   576  	}})
   577  	c.Assert(err, jc.ErrorIsNil)
   578  }
   579  
   580  func (s *RefreshConfigSuite) TestInstallOneFromRevisionBuild(c *gc.C) {
   581  	revision := 1
   582  
   583  	name := "foo"
   584  	config, err := InstallOneFromRevision(name, revision)
   585  	c.Assert(err, jc.ErrorIsNil)
   586  
   587  	config = DefineInstanceKey(c, config, "foo-bar")
   588  
   589  	req, err := config.Build()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   592  		Context: []transport.RefreshRequestContext{},
   593  		Actions: []transport.RefreshRequestAction{{
   594  			Action:      "install",
   595  			InstanceKey: "foo-bar",
   596  			Name:        &name,
   597  			Revision:    &revision,
   598  		}},
   599  		Fields: expRefreshFields,
   600  	})
   601  }
   602  
   603  func (s *RefreshConfigSuite) TestInstallOneFromRevisionFail(c *gc.C) {
   604  	_, err := InstallOneFromRevision("", 1)
   605  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   606  }
   607  
   608  func (s *RefreshConfigSuite) TestInstallOneBuildRevisionResources(c *gc.C) {
   609  	// Tests InstallOne by revision with specific resources.
   610  	revision := 1
   611  
   612  	name := "foo"
   613  	config, err := InstallOneFromRevision(name, revision)
   614  	c.Assert(err, jc.ErrorIsNil)
   615  
   616  	config = DefineInstanceKey(c, config, "foo-bar")
   617  	config, ok := AddResource(config, "testme", 3)
   618  	c.Assert(ok, jc.IsTrue)
   619  
   620  	req, err := config.Build()
   621  	c.Assert(err, jc.ErrorIsNil)
   622  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   623  		Context: []transport.RefreshRequestContext{},
   624  		Actions: []transport.RefreshRequestAction{{
   625  			Action:      "install",
   626  			InstanceKey: "foo-bar",
   627  			Name:        &name,
   628  			Revision:    &revision,
   629  			ResourceRevisions: []transport.RefreshResourceRevision{
   630  				{Name: "testme", Revision: 3},
   631  			},
   632  		}},
   633  		Fields: expRefreshFields,
   634  	})
   635  }
   636  
   637  func (s *RefreshConfigSuite) TestAddResourceFail(c *gc.C) {
   638  	config, err := RefreshOne("instance-key", "testingID", 7, "latest/edge", RefreshBase{
   639  		Name:         "ubuntu",
   640  		Channel:      "20.04",
   641  		Architecture: arch.DefaultArchitecture,
   642  	})
   643  	c.Assert(err, jc.ErrorIsNil)
   644  	_, ok := AddResource(config, "testme", 3)
   645  	c.Assert(ok, jc.IsFalse)
   646  }
   647  
   648  func (s *RefreshConfigSuite) TestInstallOneBuildChannel(c *gc.C) {
   649  	channel := "latest/stable"
   650  
   651  	name := "foo"
   652  	config, err := InstallOneFromChannel(name, channel, RefreshBase{
   653  		Name:         "ubuntu",
   654  		Channel:      "20.04",
   655  		Architecture: arch.DefaultArchitecture,
   656  	})
   657  	c.Assert(err, jc.ErrorIsNil)
   658  
   659  	config = DefineInstanceKey(c, config, "foo-bar")
   660  
   661  	req, err := config.Build()
   662  	c.Assert(err, jc.ErrorIsNil)
   663  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   664  		Context: []transport.RefreshRequestContext{},
   665  		Actions: []transport.RefreshRequestAction{{
   666  			Action:      "install",
   667  			InstanceKey: "foo-bar",
   668  			Name:        &name,
   669  			Channel:     &channel,
   670  			Base: &transport.Base{
   671  				Name:         "ubuntu",
   672  				Channel:      "20.04",
   673  				Architecture: arch.DefaultArchitecture,
   674  			},
   675  		}},
   676  		Fields: expRefreshFields,
   677  	})
   678  }
   679  
   680  func (s *RefreshConfigSuite) TestInstallOneChannelFail(c *gc.C) {
   681  	_, err := InstallOneFromChannel("", "stable", RefreshBase{
   682  		Name:         "ubuntu",
   683  		Channel:      "20.04",
   684  		Architecture: arch.DefaultArchitecture,
   685  	})
   686  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   687  }
   688  
   689  func (s *RefreshConfigSuite) TestInstallOneWithPartialPlatform(c *gc.C) {
   690  	channel := "latest/stable"
   691  
   692  	name := "foo"
   693  	config, err := InstallOneFromChannel(name, channel, RefreshBase{
   694  		Architecture: arch.DefaultArchitecture,
   695  	})
   696  	c.Assert(err, jc.ErrorIsNil)
   697  
   698  	config = DefineInstanceKey(c, config, "foo-bar")
   699  
   700  	req, err := config.Build()
   701  	c.Assert(err, jc.ErrorIsNil)
   702  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   703  		Context: []transport.RefreshRequestContext{},
   704  		Actions: []transport.RefreshRequestAction{{
   705  			Action:      "install",
   706  			InstanceKey: "foo-bar",
   707  			Name:        &name,
   708  			Channel:     &channel,
   709  			Base: &transport.Base{
   710  				Name:         notAvailable,
   711  				Channel:      notAvailable,
   712  				Architecture: arch.DefaultArchitecture,
   713  			},
   714  		}},
   715  		Fields: expRefreshFields,
   716  	})
   717  }
   718  
   719  func (s *RefreshConfigSuite) TestInstallOneWithMissingArch(c *gc.C) {
   720  	channel := "latest/stable"
   721  
   722  	name := "foo"
   723  	config, err := InstallOneFromChannel(name, channel, RefreshBase{})
   724  	c.Assert(err, jc.ErrorIsNil)
   725  
   726  	config = DefineInstanceKey(c, config, "foo-bar")
   727  
   728  	_, err = config.Build()
   729  	c.Assert(errors.IsNotValid(err), jc.IsTrue)
   730  }
   731  
   732  func (s *RefreshConfigSuite) TestInstallOneFromChannelEnsure(c *gc.C) {
   733  	config, err := InstallOneFromChannel("foo", "latest/stable", RefreshBase{
   734  		Name:         "ubuntu",
   735  		Channel:      "20.04",
   736  		Architecture: arch.DefaultArchitecture,
   737  	})
   738  	c.Assert(err, jc.ErrorIsNil)
   739  
   740  	config = DefineInstanceKey(c, config, "foo-bar")
   741  
   742  	err = config.Ensure([]transport.RefreshResponse{{
   743  		InstanceKey: "foo-bar",
   744  	}})
   745  	c.Assert(err, jc.ErrorIsNil)
   746  }
   747  
   748  func (s *RefreshConfigSuite) TestInstallOneFromChannelFail(c *gc.C) {
   749  	_, err := InstallOneFromChannel("foo", "latest/stable", RefreshBase{
   750  		Name:         "ubuntu",
   751  		Channel:      "20.04",
   752  		Architecture: arch.DefaultArchitecture,
   753  	})
   754  	c.Assert(err, jc.ErrorIsNil)
   755  }
   756  
   757  func (s *RefreshConfigSuite) TestDownloadOneFromRevisionBuild(c *gc.C) {
   758  	rev := 4
   759  	id := "foo"
   760  	config, err := DownloadOneFromRevision(id, rev)
   761  	c.Assert(err, jc.ErrorIsNil)
   762  
   763  	config = DefineInstanceKey(c, config, "foo-bar")
   764  
   765  	req, err := config.Build()
   766  	c.Assert(err, jc.ErrorIsNil)
   767  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   768  		Context: []transport.RefreshRequestContext{},
   769  		Actions: []transport.RefreshRequestAction{{
   770  			Action:      "download",
   771  			InstanceKey: "foo-bar",
   772  			ID:          &id,
   773  			Revision:    &rev,
   774  		}},
   775  		Fields: expRefreshFields,
   776  	})
   777  }
   778  
   779  func (s *RefreshConfigSuite) TestDownloadOneFromRevisionFail(c *gc.C) {
   780  	_, err := DownloadOneFromRevision("", 7)
   781  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   782  }
   783  
   784  func (s *RefreshConfigSuite) TestDownloadOneFromRevisionByNameBuild(c *gc.C) {
   785  	rev := 4
   786  	name := "foo"
   787  	config, err := DownloadOneFromRevisionByName(name, rev)
   788  	c.Assert(err, jc.ErrorIsNil)
   789  
   790  	config = DefineInstanceKey(c, config, "foo-bar")
   791  
   792  	req, err := config.Build()
   793  	c.Assert(err, jc.ErrorIsNil)
   794  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   795  		Context: []transport.RefreshRequestContext{},
   796  		Actions: []transport.RefreshRequestAction{{
   797  			Action:      "download",
   798  			InstanceKey: "foo-bar",
   799  			Name:        &name,
   800  			Revision:    &rev,
   801  		}},
   802  		Fields: expRefreshFields,
   803  	})
   804  }
   805  
   806  func (s *RefreshConfigSuite) TestDownloadOneFromRevisionByNameFail(c *gc.C) {
   807  	_, err := DownloadOneFromRevisionByName("", 7)
   808  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   809  }
   810  
   811  func (s *RefreshConfigSuite) TestDownloadOneFromChannelBuild(c *gc.C) {
   812  	channel := "latest/stable"
   813  	id := "foo"
   814  	config, err := DownloadOneFromChannel(id, channel, RefreshBase{
   815  		Name:         "ubuntu",
   816  		Channel:      "20.04",
   817  		Architecture: arch.DefaultArchitecture,
   818  	})
   819  	c.Assert(err, jc.ErrorIsNil)
   820  
   821  	config = DefineInstanceKey(c, config, "foo-bar")
   822  
   823  	req, err := config.Build()
   824  	c.Assert(err, jc.ErrorIsNil)
   825  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   826  		Context: []transport.RefreshRequestContext{},
   827  		Actions: []transport.RefreshRequestAction{{
   828  			Action:      "download",
   829  			InstanceKey: "foo-bar",
   830  			ID:          &id,
   831  			Channel:     &channel,
   832  			Base: &transport.Base{
   833  				Name:         "ubuntu",
   834  				Channel:      "20.04",
   835  				Architecture: arch.DefaultArchitecture,
   836  			},
   837  		}},
   838  		Fields: expRefreshFields,
   839  	})
   840  }
   841  
   842  func (s *RefreshConfigSuite) TestDownloadOneFromChannelFail(c *gc.C) {
   843  	_, err := DownloadOneFromChannel("", "latest/stable", RefreshBase{
   844  		Name:         "ubuntu",
   845  		Channel:      "20.04",
   846  		Architecture: arch.DefaultArchitecture,
   847  	})
   848  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   849  }
   850  
   851  func (s *RefreshConfigSuite) TestDownloadOneFromChannelByNameBuild(c *gc.C) {
   852  	channel := "latest/stable"
   853  	name := "foo"
   854  	config, err := DownloadOneFromChannelByName(name, channel, RefreshBase{
   855  		Name:         "ubuntu",
   856  		Channel:      "20.04",
   857  		Architecture: arch.DefaultArchitecture,
   858  	})
   859  	c.Assert(err, jc.ErrorIsNil)
   860  
   861  	config = DefineInstanceKey(c, config, "foo-bar")
   862  
   863  	req, err := config.Build()
   864  	c.Assert(err, jc.ErrorIsNil)
   865  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   866  		Context: []transport.RefreshRequestContext{},
   867  		Actions: []transport.RefreshRequestAction{{
   868  			Action:      "download",
   869  			InstanceKey: "foo-bar",
   870  			Name:        &name,
   871  			Channel:     &channel,
   872  			Base: &transport.Base{
   873  				Name:         "ubuntu",
   874  				Channel:      "20.04",
   875  				Architecture: arch.DefaultArchitecture,
   876  			},
   877  		}},
   878  		Fields: expRefreshFields,
   879  	})
   880  }
   881  
   882  func (s *RefreshConfigSuite) TestDownloadOneFromChannelByNameFail(c *gc.C) {
   883  	_, err := DownloadOneFromChannel("", "latest/stable", RefreshBase{
   884  		Name:         "ubuntu",
   885  		Channel:      "20.04",
   886  		Architecture: arch.DefaultArchitecture,
   887  	})
   888  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   889  }
   890  
   891  func (s *RefreshConfigSuite) TestDownloadOneFromChannelBuildK8s(c *gc.C) {
   892  	channel := "latest/stable"
   893  	id := "foo"
   894  	config, err := DownloadOneFromChannel(id, channel, RefreshBase{
   895  		Name:         "kubernetes",
   896  		Channel:      "kubernetes",
   897  		Architecture: arch.DefaultArchitecture,
   898  	})
   899  	c.Assert(err, jc.ErrorIsNil)
   900  
   901  	config = DefineInstanceKey(c, config, "foo-bar")
   902  
   903  	req, err := config.Build()
   904  	c.Assert(err, jc.ErrorIsNil)
   905  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
   906  		Context: []transport.RefreshRequestContext{},
   907  		Actions: []transport.RefreshRequestAction{{
   908  			Action:      "download",
   909  			InstanceKey: "foo-bar",
   910  			ID:          &id,
   911  			Channel:     &channel,
   912  			Base: &transport.Base{
   913  				Name:         "ubuntu",
   914  				Channel:      "22.04",
   915  				Architecture: arch.DefaultArchitecture,
   916  			},
   917  		}},
   918  		Fields: expRefreshFields,
   919  	})
   920  }
   921  
   922  func (s *RefreshConfigSuite) TestDownloadOneFromChannelEnsure(c *gc.C) {
   923  	config, err := DownloadOneFromChannel("foo", "latest/stable", RefreshBase{
   924  		Name:         "ubuntu",
   925  		Channel:      "20.04",
   926  		Architecture: arch.DefaultArchitecture,
   927  	})
   928  	c.Assert(err, jc.ErrorIsNil)
   929  
   930  	config = DefineInstanceKey(c, config, "foo-bar")
   931  
   932  	err = config.Ensure([]transport.RefreshResponse{{
   933  		InstanceKey: "foo-bar",
   934  	}})
   935  	c.Assert(err, jc.ErrorIsNil)
   936  }
   937  
   938  func (s *RefreshConfigSuite) TestRefreshManyBuildContextNotNil(c *gc.C) {
   939  	id1 := "foo"
   940  	config1, err := DownloadOneFromRevision(id1, 1)
   941  	c.Assert(err, jc.ErrorIsNil)
   942  	config1 = DefineInstanceKey(c, config1, "foo-bar")
   943  
   944  	id2 := "bar"
   945  	config2, err := DownloadOneFromChannel(id2, "latest/edge", RefreshBase{
   946  		Name:         "ubuntu",
   947  		Channel:      "14.04",
   948  		Architecture: arch.DefaultArchitecture,
   949  	})
   950  	c.Assert(err, jc.ErrorIsNil)
   951  	config2 = DefineInstanceKey(c, config2, "foo-baz")
   952  	config := RefreshMany(config1, config2)
   953  
   954  	req, err := config.Build()
   955  	c.Assert(err, jc.ErrorIsNil)
   956  	c.Assert(req.Context, gc.NotNil)
   957  }
   958  
   959  func (s *RefreshConfigSuite) TestRefreshManyBuild(c *gc.C) {
   960  	id1 := "foo"
   961  	config1, err := RefreshOne("instance-key", id1, 1, "latest/stable", RefreshBase{
   962  		Name:         "ubuntu",
   963  		Channel:      "20.04",
   964  		Architecture: arch.DefaultArchitecture,
   965  	})
   966  	c.Assert(err, jc.ErrorIsNil)
   967  
   968  	id2 := "bar"
   969  	config2, err := RefreshOne("instance-key2", id2, 2, "latest/edge", RefreshBase{
   970  		Name:         "ubuntu",
   971  		Channel:      "14.04",
   972  		Architecture: arch.DefaultArchitecture,
   973  	})
   974  	c.Assert(err, jc.ErrorIsNil)
   975  
   976  	channel := "1/stable"
   977  
   978  	name3 := "baz"
   979  	config3, err := InstallOneFromChannel(name3, "1/stable", RefreshBase{
   980  		Name:         "ubuntu",
   981  		Channel:      "19.04",
   982  		Architecture: arch.DefaultArchitecture,
   983  	})
   984  	c.Assert(err, jc.ErrorIsNil)
   985  
   986  	config3 = DefineInstanceKey(c, config3, "foo-taz")
   987  
   988  	name4 := "forty-two"
   989  	rev4 := 42
   990  	config4, err := InstallOneFromRevision(name4, rev4)
   991  	c.Assert(err, jc.ErrorIsNil)
   992  
   993  	config4 = DefineInstanceKey(c, config4, "foo-two")
   994  
   995  	config := RefreshMany(config1, config2, config3, config4)
   996  
   997  	req, err := config.Build()
   998  	c.Assert(err, jc.ErrorIsNil)
   999  	c.Assert(req, gc.DeepEquals, transport.RefreshRequest{
  1000  		Context: []transport.RefreshRequestContext{{
  1001  			InstanceKey: "instance-key",
  1002  			ID:          "foo",
  1003  			Revision:    1,
  1004  			Base: transport.Base{
  1005  				Name:         "ubuntu",
  1006  				Channel:      "20.04",
  1007  				Architecture: arch.DefaultArchitecture,
  1008  			},
  1009  			TrackingChannel: "latest/stable",
  1010  		}, {
  1011  			InstanceKey: "instance-key2",
  1012  			ID:          "bar",
  1013  			Revision:    2,
  1014  			Base: transport.Base{
  1015  				Name:         "ubuntu",
  1016  				Channel:      "14.04",
  1017  				Architecture: arch.DefaultArchitecture,
  1018  			},
  1019  			TrackingChannel: "latest/edge",
  1020  		}},
  1021  		Actions: []transport.RefreshRequestAction{{
  1022  			Action:      "refresh",
  1023  			InstanceKey: "instance-key",
  1024  			ID:          &id1,
  1025  		}, {
  1026  			Action:      "refresh",
  1027  			InstanceKey: "instance-key2",
  1028  			ID:          &id2,
  1029  		}, {
  1030  			Action:      "install",
  1031  			InstanceKey: "foo-taz",
  1032  			Name:        &name3,
  1033  			Base: &transport.Base{
  1034  				Name:         "ubuntu",
  1035  				Channel:      "19.04",
  1036  				Architecture: arch.DefaultArchitecture,
  1037  			},
  1038  			Channel: &channel,
  1039  		}, {
  1040  			Action:      "install",
  1041  			InstanceKey: "foo-two",
  1042  			Name:        &name4,
  1043  			Revision:    &rev4,
  1044  		}},
  1045  		Fields: expRefreshFields,
  1046  	})
  1047  }
  1048  
  1049  func (s *RefreshConfigSuite) TestRefreshManyEnsure(c *gc.C) {
  1050  	config1, err := RefreshOne("instance-key", "foo", 1, "latest/stable", RefreshBase{
  1051  		Name:         "ubuntu",
  1052  		Channel:      "20.04",
  1053  		Architecture: arch.DefaultArchitecture,
  1054  	})
  1055  	c.Assert(err, jc.ErrorIsNil)
  1056  
  1057  	config2, err := RefreshOne("instance-key2", "bar", 2, "latest/edge", RefreshBase{
  1058  		Name:         "ubuntu",
  1059  		Channel:      "14.04",
  1060  		Architecture: arch.DefaultArchitecture,
  1061  	})
  1062  	c.Assert(err, jc.ErrorIsNil)
  1063  
  1064  	config := RefreshMany(config1, config2)
  1065  
  1066  	err = config.Ensure([]transport.RefreshResponse{{
  1067  		InstanceKey: "instance-key",
  1068  	}, {
  1069  		InstanceKey: "instance-key2",
  1070  	}})
  1071  	c.Assert(err, jc.ErrorIsNil)
  1072  }