diff --git a/.github/verify-apache2.sh b/.github/verify-apache2.sh new file mode 100755 index 00000000000..ea3d356f24b --- /dev/null +++ b/.github/verify-apache2.sh @@ -0,0 +1,7 @@ +#!/usr/bin/bash + +set -euxo pipefail + +ip=$(juju status --format json | jq -r '.applications.apache2.units[]."public-address"') + +curl --silent --output /dev/null --max-time 3 "$ip" diff --git a/.github/workflows/client-tests.yml b/.github/workflows/client-tests.yml index 1d89734be29..84c9158ab9f 100644 --- a/.github/workflows/client-tests.yml +++ b/.github/workflows/client-tests.yml @@ -48,14 +48,6 @@ jobs: - name: "Install Mongo Dependencies: ubuntu-latest" if: (matrix.os == 'ubuntu-latest') run: | - # Remove the default mongo - for version in "4.2" "4.4"; do - sudo rm "/etc/apt/sources.list.d/mongodb-org-${version}.list" || true - done - sudo DEBIAN_FRONTEND=noninteractive apt-get purge -y mongodb-org - sudo DEBIAN_FRONTEND=noninteractive apt autoremove - sudo rm -rf /usr/bin/mongo* || true - make install-mongo-dependencies - name: "Remove Mongo Dependencies: windows-latest" @@ -68,18 +60,18 @@ jobs: if: (matrix.os == 'windows-latest') uses: crazy-max/ghaction-chocolatey@v1 with: - args: install mongodb.install --version=4.0.21 --allow-downgrade + args: install mongodb.install --version=4.4.11 --allow-downgrade # GitHub runners already have preinstalled version of mongodb, but - # we specifically need 4.0.21, otherwise our tests will not pass + # we specifically need 4.4.11, otherwise our tests will not pass - name: "Install Mongo Dependencies: macOS-latest" if: (matrix.os == 'macOS-latest') run: | - curl -o mongodb-4.0.21.tgz https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.21.tgz - tar xzvf mongodb-4.0.21.tgz + curl -o mongodb-4.4.11.tgz https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.4.11.tgz + tar xzvf mongodb-4.4.11.tgz sudo rm -rf /usr/local/mongodb sudo mkdir -p /usr/local/mongodb - sudo mv mongodb-osx-x86_64-4.0.21/bin/* /usr/local/mongodb + sudo mv mongodb-macos-x86_64-4.4.11/bin/* /usr/local/mongodb sudo mkdir -p /usr/local/bin sudo rm /usr/local/bin/mongod sudo ln -s /usr/local/mongodb/mongod /usr/local/bin/mongod diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 671567fb789..0bfffab563a 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -77,7 +77,7 @@ jobs: sudo chmod a+wr /var/snap/lxd/common/lxd/unix.socket echo "/snap/bin" >> $GITHUB_PATH - - name: Bootstrap Juju latest/stable + - name: Bootstrap Juju if: env.RUN_TEST == 'RUN' shell: bash run: | @@ -92,6 +92,26 @@ jobs: if: env.RUN_TEST == 'RUN' uses: actions/checkout@v2 + - name: Deploy some applications + if: env.RUN_TEST == 'RUN' + shell: bash + run: | + set -euxo pipefail + + juju deploy apache2 --series focal + + juju wait-for application apache2 + + attempts=0 + while ! ./.github/verify-apache2.sh; do + sleep 10 + attempts=$((attempts+1)) + if [ "$attempts" -eq 5 ]; then + echo "Apache health check timed out" + exit 0 + fi + done + - name: Build snap if: env.RUN_TEST == 'RUN' shell: bash @@ -104,7 +124,7 @@ jobs: shell: bash run: | set -euxo pipefail - sudo snap install *.snap --dangerous --classic + sudo snap install juju*.snap --dangerous --classic - name: Preflight if: env.RUN_TEST == 'RUN' @@ -144,6 +164,8 @@ jobs: exit 1 fi + ./.github/verify-apache2.sh + - name: Test upgrade model if: env.RUN_TEST == 'RUN' shell: bash @@ -194,3 +216,5 @@ jobs: juju debug-log --replay --no-tail exit 1 fi + + ./.github/verify-apache2.sh diff --git a/Makefile b/Makefile index 98afa304062..06704d46f9b 100644 --- a/Makefile +++ b/Makefile @@ -181,10 +181,11 @@ endif WAIT_FOR_DPKG=sh -c '. "${PROJECT_DIR}/make_functions.sh"; wait_for_dpkg "$$@"' wait_for_dpkg +JUJU_DB_CHANNEL=4.4/stable install-mongo-dependencies: ## install-mongo-dependencies: Install Mongo and its dependencies - @echo Installing 4.4 juju-db snap for mongodb - @sudo snap install juju-db --channel=4.4/stable + @echo Installing ${JUJU_DB_CHANNEL} juju-db snap for mongodb + @sudo snap refresh juju-db --channel=${JUJU_DB_CHANNEL} 2> /dev/null; sudo snap install juju-db --channel=${JUJU_DB_CHANNEL} 2> /dev/null @$(WAIT_FOR_DPKG) @sudo apt-get --yes install $(strip $(DEPENDENCIES)) diff --git a/README.md b/README.md index 7d8ae726a05..729e7f31440 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,8 @@ centralised enterprise grade management and operations systems. Our community hangs out at the [Charmhub discourse](https://discourse.juju.is/) which serves as a combination mailing list and web forum. Keep up with the news and get a feel for operator engineering and usage there. Get the Juju CLI on -Windows, macOS or Linux with the -[install instructions](https://juju.is/docs/installing) and +Windows, macOS or Linux with the +[install instructions](https://juju.is/docs/installing) and [try the tutorials](https://juju.is/docs/tutorials). All you need is a small K8s cluster, or an Ubuntu machine or VM to run MicroK8s. @@ -126,5 +126,5 @@ of commands and usage. Follow our [code and contribution guidelines](CONTRIBUTING.md) to learn how to make code changes. File bugs in [Launchpad](https://bugs.launchpad.net/juju/+filebug) or ask questions on -our [Freenode IRC channel](https://webchat.freenode.net/#juju), and +our [Freenode IRC channel](https://webchat.freenode.net/#juju), and [Mattermost](chat.charmhub.io/). diff --git a/agent/agentbootstrap/bootstrap_test.go b/agent/agentbootstrap/bootstrap_test.go index c4b5367e25b..3fc64f61e03 100644 --- a/agent/agentbootstrap/bootstrap_test.go +++ b/agent/agentbootstrap/bootstrap_test.go @@ -126,7 +126,7 @@ LXC_BRIDGE="ignored"`[1:]) expectBootstrapConstraints := constraints.MustParse("mem=1024M") expectModelConstraints := constraints.MustParse("mem=512M") expectHW := instance.MustParseHardware("mem=2048M") - initialAddrs := corenetwork.NewProviderAddresses( + initialAddrs := corenetwork.NewMachineAddresses([]string{ "zeroonetwothree", "0.1.2.3", "10.0.3.1", // lxc bridge address filtered. @@ -135,7 +135,7 @@ LXC_BRIDGE="ignored"`[1:]) "10.0.4.1", // lxd bridge address filtered. "10.0.4.4", // lxd bridge address filtered. "10.0.4.5", // not an lxd bridge address - ) + }).AsProviderAddresses() filteredAddrs := corenetwork.NewSpaceAddresses( "zeroonetwothree", "0.1.2.3", diff --git a/api/charmhub/client.go b/api/charmhub/client.go deleted file mode 100644 index 510f2fdf456..00000000000 --- a/api/charmhub/client.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "github.com/juju/errors" - "github.com/juju/names/v4" - - "github.com/juju/juju/api/base" - "github.com/juju/juju/apiserver/params" -) - -const charmHubFacade = "CharmHub" - -// Client allows access to the CharmHub API end point. -type Client struct { - base.ClientFacade - facade base.FacadeCaller -} - -// NewClient creates a new client for accessing the CharmHub API. -func NewClient(callCloser base.APICallCloser) *Client { - frontend, backend := base.NewClientFacade(callCloser, charmHubFacade) - return newClientFromFacade(frontend, backend) -} - -// NewClientFromFacade creates a new charmHub client using the input -// client facade and facade caller. -func newClientFromFacade(frontend base.ClientFacade, backend base.FacadeCaller) *Client { - return &Client{ - ClientFacade: frontend, - facade: backend, - } -} - -// Info queries the CharmHub API for information for a given name. -func (c *Client) Info(name string, options ...InfoOption) (InfoResponse, error) { - opts := newInfoOptions() - for _, option := range options { - option(opts) - } - - args := params.Info{ - Tag: names.NewApplicationTag(name).String(), - Channel: opts.channel, - } - var result params.CharmHubEntityInfoResult - if err := c.facade.FacadeCall("Info", args, &result); err != nil { - return InfoResponse{}, errors.Trace(err) - } - - return convertCharmInfoResult(result.Result), nil -} - -// Find queries the CharmHub API finding potential charms or bundles for the -// given query. -func (c *Client) Find(query string, options ...FindOption) ([]FindResponse, error) { - opts := newFindOptions() - for _, option := range options { - option(opts) - } - - args := params.Query{ - Query: query, - Category: opts.category, - Channel: opts.channel, - CharmType: opts.charmType, - Platforms: opts.platforms, - Publisher: opts.publisher, - RelationRequires: opts.relationRequires, - RelationProvides: opts.relationProvides, - } - - var result params.CharmHubEntityFindResult - if err := c.facade.FacadeCall("Find", args, &result); err != nil { - return nil, errors.Trace(err) - } - - return convertCharmFindResults(result.Results), nil -} - -// InfoOption to be passed to Info to customize the resulting request. -type InfoOption func(*infoOptions) - -type infoOptions struct { - channel string -} - -// WithInfoChannel sets the channel on the option. -func WithInfoChannel(ch string) InfoOption { - return func(infoOptions *infoOptions) { - infoOptions.channel = ch - } -} - -// Create a infoOptions instance with default values. -func newInfoOptions() *infoOptions { - return &infoOptions{} -} - -// FindOption to be passed to Find to customize the resulting request. -type FindOption func(*findOptions) - -type findOptions struct { - category string - channel string - charmType string - platforms string - publisher string - relationRequires string - relationProvides string -} - -// WithFindCategory sets the category on the option. -func WithFindCategory(category string) FindOption { - return func(findOptions *findOptions) { - findOptions.category = category - } -} - -// WithFindChannel sets the channel on the option. -func WithFindChannel(channel string) FindOption { - return func(findOptions *findOptions) { - findOptions.channel = channel - } -} - -// WithFindType sets the charmType on the option. -func WithFindType(charmType string) FindOption { - return func(findOptions *findOptions) { - findOptions.charmType = charmType - } -} - -// WithFindPlatforms sets the charmPlatforms on the option. -func WithFindPlatforms(platforms string) FindOption { - return func(findOptions *findOptions) { - findOptions.platforms = platforms - } -} - -// WithFindPublisher sets the publisher on the option. -func WithFindPublisher(publisher string) FindOption { - return func(findOptions *findOptions) { - findOptions.publisher = publisher - } -} - -// WithFindRelationRequires sets the relationRequires on the option. -func WithFindRelationRequires(relationRequires string) FindOption { - return func(findOptions *findOptions) { - findOptions.relationRequires = relationRequires - } -} - -// WithFindRelationProvides sets the relationProvides on the option. -func WithFindRelationProvides(relationProvides string) FindOption { - return func(findOptions *findOptions) { - findOptions.relationProvides = relationProvides - } -} - -// Create a findOptions instance with default values. -func newFindOptions() *findOptions { - return &findOptions{} -} diff --git a/api/charmhub/client_test.go b/api/charmhub/client_test.go deleted file mode 100644 index 3eaeb826416..00000000000 --- a/api/charmhub/client_test.go +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "github.com/golang/mock/gomock" - "github.com/juju/charm/v9" - "github.com/juju/names/v4" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/api/base/mocks" - "github.com/juju/juju/apiserver/params" -) - -type charmHubSuite struct { - client *mocks.MockClientFacade - facade *mocks.MockFacadeCaller -} - -var _ = gc.Suite(&charmHubSuite{}) - -func (s charmHubSuite) TestInfo(c *gc.C) { - defer s.setupMocks(c).Finish() - - arg := params.Info{Tag: names.NewApplicationTag("wordpress").String()} - resultSource := params.CharmHubEntityInfoResult{ - Result: getParamsInfoResponse(), - } - s.facade.EXPECT().FacadeCall("Info", arg, gomock.Any()).SetArg(2, resultSource) - - obtained, err := s.newClientFromFacadeForTest().Info("wordpress") - c.Assert(err, jc.ErrorIsNil) - assertInfoResponseSameContents(c, obtained, getInfoResponse()) -} - -func (s charmHubSuite) TestInfoWithOptions(c *gc.C) { - defer s.setupMocks(c).Finish() - - arg := params.Info{ - Tag: names.NewApplicationTag("wordpress").String(), - Channel: "stable", - } - resultSource := params.CharmHubEntityInfoResult{ - Result: getParamsInfoResponse(), - } - s.facade.EXPECT().FacadeCall("Info", arg, gomock.Any()).SetArg(2, resultSource) - - obtained, err := s.newClientFromFacadeForTest().Info("wordpress", WithInfoChannel("stable")) - c.Assert(err, jc.ErrorIsNil) - assertInfoResponseSameContents(c, obtained, getInfoResponse()) -} - -func (s charmHubSuite) TestFind(c *gc.C) { - defer s.setupMocks(c).Finish() - - arg := params.Query{Query: "wordpress"} - resultSource := params.CharmHubEntityFindResult{ - Results: getParamsFindResponses(), - } - s.facade.EXPECT().FacadeCall("Find", arg, gomock.Any()).SetArg(2, resultSource) - - obtained, err := s.newClientFromFacadeForTest().Find("wordpress") - c.Assert(err, jc.ErrorIsNil) - assertFindResponsesSameContents(c, obtained, getFindResponses()) -} - -func (s charmHubSuite) TestFindWithOptions(c *gc.C) { - defer s.setupMocks(c).Finish() - - arg := params.Query{ - Query: "wordpress", - Channel: "stable", - Platforms: "platforms", - } - resultSource := params.CharmHubEntityFindResult{ - Results: getParamsFindResponses(), - } - s.facade.EXPECT().FacadeCall("Find", arg, gomock.Any()).SetArg(2, resultSource) - - obtained, err := s.newClientFromFacadeForTest().Find("wordpress", WithFindChannel("stable"), WithFindPlatforms("platforms")) - c.Assert(err, jc.ErrorIsNil) - assertFindResponsesSameContents(c, obtained, getFindResponses()) -} - -func (s *charmHubSuite) newClientFromFacadeForTest() *Client { - return newClientFromFacade(s.client, s.facade) -} - -func (s *charmHubSuite) setupMocks(c *gc.C) *gomock.Controller { - ctrl := gomock.NewController(c) - - s.client = mocks.NewMockClientFacade(ctrl) - s.facade = mocks.NewMockFacadeCaller(ctrl) - - return ctrl -} - -func getInfoResponse() InfoResponse { - return InfoResponse{ - Name: "wordpress", - Type: "charm", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Description: "This will install and setup WordPress optimized to run in the cloud.\nBy default it will place Ngnix configured to scale horizontally\nwith Nginx's reverse proxy.", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Tracks: []string{"latest"}, - Series: []string{"bionic", "xenial"}, - StoreURL: "https://someurl.com/wordpress", - Tags: []string{"app", "seven"}, - Channels: map[string]Channel{ - "latest/stable": { - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Track: "latest", - Risk: "stable", - Size: 12042240, - Revision: 16, - Version: "1.0.3", - }}, - Charm: &Charm{ - Subordinate: false, - Config: &charm.Config{ - Options: map[string]charm.Option{ - "reticulate-splines": {Type: "boolean", Description: "Whether to reticulate splines on launch, or not."}, - "title": {Type: "string", Description: "A descriptive title used for the application.", Default: "My Title"}, - "subtitle": {Type: "string", Description: "An optional subtitle used for the application.", Default: ""}, - "outlook": {Type: "string", Description: "No default outlook."}, - "username": {Type: "string", Description: "The name of the initial account (given admin permissions).", Default: "admin001"}, - "skill-level": {Type: "int", Description: "A number indicating skill."}, - "agility-ratio": {Type: "float", Description: "A number from 0 to 1 indicating agility."}, - }, - }, - Relations: map[string]map[string]string{ - "provides": {"source": "dummy-token"}, - "requires": {"sink": "dummy-token"}}, - UsedBy: []string{ - "wordpress-everlast", - "wordpress-jorge", - "wordpress-site", - }, - }, - } -} - -func getFindResponses() []FindResponse { - return []FindResponse{{ - Name: "wordpress", - Type: "object", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Version: "1.0.3", - Series: []string{"bionic"}, - StoreURL: "https://someurl.com/wordpress", - }} -} - -func assertInfoResponseSameContents(c *gc.C, obtained, expected InfoResponse) { - c.Assert(obtained.Type, gc.Equals, expected.Type) - c.Assert(obtained.ID, gc.Equals, expected.ID) - c.Assert(obtained.Name, gc.Equals, expected.Name) - c.Assert(obtained.Description, gc.Equals, expected.Description) - c.Assert(obtained.Publisher, jc.DeepEquals, expected.Publisher) - c.Assert(obtained.Summary, gc.Equals, expected.Summary) - c.Assert(obtained.Tags, gc.DeepEquals, expected.Tags) - c.Assert(obtained.Channels, jc.DeepEquals, expected.Channels) - c.Assert(obtained.Tracks, jc.SameContents, expected.Tracks) - assertCharmSameContents(c, obtained.Charm, expected.Charm) -} - -func assertCharmSameContents(c *gc.C, obtained, expected *Charm) { - c.Assert(obtained.Config, gc.DeepEquals, expected.Config) - c.Assert(obtained.Relations, jc.DeepEquals, expected.Relations) - c.Assert(obtained.Subordinate, gc.Equals, expected.Subordinate) - c.Assert(obtained.UsedBy, gc.DeepEquals, expected.UsedBy) -} - -func assertFindResponsesSameContents(c *gc.C, obtained, expected []FindResponse) { - c.Assert(obtained, gc.HasLen, 1) - c.Assert(expected, gc.HasLen, 1) - want := obtained[0] - got := expected[0] - c.Assert(want.Type, gc.Equals, got.Type) - c.Assert(want.ID, gc.Equals, got.ID) - c.Assert(want.Name, gc.Equals, got.Name) - c.Assert(want.Publisher, gc.Equals, got.Publisher) - c.Assert(want.Summary, gc.Equals, got.Summary) - c.Assert(want.Version, gc.Equals, got.Version) - c.Assert(want.Series, gc.DeepEquals, got.Series) - c.Assert(want.StoreURL, gc.Equals, got.StoreURL) -} - -func getParamsInfoResponse() params.InfoResponse { - return params.InfoResponse{ - Name: "wordpress", - Type: "charm", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Description: "This will install and setup WordPress optimized to run in the cloud.\nBy default it will place Ngnix configured to scale horizontally\nwith Nginx's reverse proxy.", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Tracks: []string{"latest"}, - Series: []string{"bionic", "xenial"}, - StoreURL: "https://someurl.com/wordpress", - Tags: []string{"app", "seven"}, - Channels: map[string]params.Channel{ - "latest/stable": { - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Track: "latest", - Risk: "stable", - Size: 12042240, - Revision: 16, - Version: "1.0.3", - }}, - Charm: ¶ms.CharmHubCharm{ - Subordinate: false, - Config: map[string]params.CharmOption{ - "reticulate-splines": {Type: "boolean", Description: "Whether to reticulate splines on launch, or not."}, - "title": {Type: "string", Description: "A descriptive title used for the application.", Default: "My Title"}, - "subtitle": {Type: "string", Description: "An optional subtitle used for the application.", Default: ""}, - "outlook": {Type: "string", Description: "No default outlook."}, - "username": {Type: "string", Description: "The name of the initial account (given admin permissions).", Default: "admin001"}, - "skill-level": {Type: "int", Description: "A number indicating skill."}, - "agility-ratio": {Type: "float", Description: "A number from 0 to 1 indicating agility."}, - }, - Relations: map[string]map[string]string{ - "provides": {"source": "dummy-token"}, - "requires": {"sink": "dummy-token"}}, - UsedBy: []string{ - "wordpress-everlast", - "wordpress-jorge", - "wordpress-site", - }, - }, - } -} - -func getParamsFindResponses() []params.FindResponse { - return []params.FindResponse{{ - Type: "object", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Name: "wordpress", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Version: "1.0.3", - Series: []string{"bionic"}, - StoreURL: "https://someurl.com/wordpress", - }} -} diff --git a/api/charmhub/data.go b/api/charmhub/data.go deleted file mode 100644 index faadfd9ccfe..00000000000 --- a/api/charmhub/data.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "github.com/juju/charm/v9" - "github.com/juju/loggo" - - "github.com/juju/juju/apiserver/params" -) - -var logger = loggo.GetLogger("juju.api.charmhub") - -func convertCharmInfoResult(info params.InfoResponse) InfoResponse { - ir := InfoResponse{ - Type: info.Type, - ID: info.ID, - Name: info.Name, - Description: info.Description, - Publisher: info.Publisher, - Summary: info.Summary, - Series: info.Series, - StoreURL: info.StoreURL, - Tags: info.Tags, - Channels: convertChannels(info.Channels), - Tracks: info.Tracks, - } - switch ir.Type { - case "bundle": - ir.Bundle = convertBundle(info.Bundle) - case "charm": - ir.Charm = convertCharm(info.Charm) - } - return ir -} - -func convertCharmFindResults(responses []params.FindResponse) []FindResponse { - results := make([]FindResponse, len(responses)) - for i, resp := range responses { - results[i] = convertCharmFindResult(resp) - } - return results -} - -func convertCharmFindResult(resp params.FindResponse) FindResponse { - return FindResponse{ - Type: resp.Type, - ID: resp.ID, - Name: resp.Name, - Publisher: resp.Publisher, - Summary: resp.Summary, - Version: resp.Version, - Arches: resp.Arches, - OS: resp.OS, - Series: resp.Series, - StoreURL: resp.StoreURL, - } -} - -func convertBundle(in interface{}) *Bundle { - inB, ok := in.(*params.CharmHubBundle) - if !ok { - logger.Errorf("unexpected: CharmHubBundle is not a bundle") - return nil - } - if inB == nil { - logger.Errorf("CharmHubBundle is nil") - return nil - } - out := Bundle{ - Charms: make([]BundleCharm, len(inB.Charms)), - } - for i, c := range inB.Charms { - out.Charms[i] = BundleCharm(c) - } - return &out -} - -func convertCharm(in interface{}) *Charm { - inC, ok := in.(*params.CharmHubCharm) - if !ok { - logger.Errorf("unexpected: CharmHubCharm is not a charm") - return nil - } - if inC == nil { - logger.Errorf("CharmHubCharm is nil") - return nil - } - return &Charm{ - Config: params.FromCharmOptionMap(inC.Config), - Relations: inC.Relations, - Subordinate: inC.Subordinate, - UsedBy: inC.UsedBy, - } -} - -func convertChannels(in map[string]params.Channel) map[string]Channel { - out := make(map[string]Channel, len(in)) - for k, v := range in { - out[k] = Channel{ - ReleasedAt: v.ReleasedAt, - Track: v.Track, - Risk: v.Risk, - Revision: v.Revision, - Size: v.Size, - Version: v.Version, - Platforms: convertPlatforms(v.Platforms), - } - } - return out -} - -func convertPlatforms(in []params.Platform) []Platform { - out := make([]Platform, len(in)) - for i, v := range in { - out[i] = Platform(v) - } - return out -} - -// Although InfoResponse or FindResponse are similar, they will change once the -// CharmHub API has settled. - -type InfoResponse struct { - Type string `json:"type"` - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Publisher string `json:"publisher"` - Summary string `json:"summary"` - Series []string `json:"series,omitempty"` - StoreURL string `json:"store-url"` - Tags []string `json:"tags,omitempty"` - Charm *Charm `json:"charm,omitempty"` - Bundle *Bundle `json:"bundle,omitempty"` - Channels map[string]Channel `json:"channel-map"` - Tracks []string `json:"tracks"` -} - -type FindResponse struct { - Type string `json:"type"` - ID string `json:"id"` - Name string `json:"name"` - Publisher string `json:"publisher"` - Summary string `json:"summary"` - Version string `json:"version"` - Arches []string `json:"architectures,omitempty"` - OS []string `json:"os,omitempty"` - Series []string `json:"series,omitempty"` - StoreURL string `json:"store-url"` -} - -type Channel struct { - ReleasedAt string `json:"released-at"` - Track string `json:"track"` - Risk string `json:"risk"` - Revision int `json:"revision"` - Size int `json:"size"` - Version string `json:"version"` - Platforms []Platform `json:"platforms"` - Resources []Resource `json:"resources"` -} - -type Platform struct { - Architecture string `json:"architecture"` - OS string `json:"os"` - Series string `json:"series"` -} - -type Resource struct { - Name string `json:"name"` - Revision int `json:"revision"` - Type string `json:"type"` - Size int `json:"size"` - URL string `json:"url"` -} - -// Charm matches a params.CharmHubCharm -type Charm struct { - Config *charm.Config `json:"config,omitempty"` - Relations map[string]map[string]string `json:"relations,omitempty"` - Subordinate bool `json:"subordinate,omitempty"` - UsedBy []string `json:"used-by,omitempty"` // bundles which use the charm -} - -type Bundle struct { - Charms []BundleCharm `json:"charms,omitempty"` -} - -type BundleCharm struct { - Name string `json:"name"` - PackageID string `json:"package-id"` -} diff --git a/api/charmhub/package_test.go b/api/charmhub/package_test.go deleted file mode 100644 index 5b40c8393e4..00000000000 --- a/api/charmhub/package_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "testing" - - gc "gopkg.in/check.v1" -) - -func TestPackage(t *testing.T) { - gc.TestingT(t) -} diff --git a/api/facadeversions.go b/api/facadeversions.go index 0eeacbe28f0..7ed6d913bde 100644 --- a/api/facadeversions.go +++ b/api/facadeversions.go @@ -36,7 +36,6 @@ var facadeVersions = map[string]int{ "CAASOperatorProvisioner": 1, "CAASOperatorUpgrader": 1, "CAASUnitProvisioner": 2, - "CharmHub": 1, "CharmDownloader": 1, "CharmRevisionUpdater": 2, "Charms": 4, diff --git a/api/instancepoller/machine_test.go b/api/instancepoller/machine_test.go index 7b056bdfd88..11900507fb9 100644 --- a/api/instancepoller/machine_test.go +++ b/api/instancepoller/machine_test.go @@ -229,7 +229,7 @@ func (s *MachineSuite) TestSetProviderNetworkConfigSuccess(c *gc.C) { cfg := network.InterfaceInfos{{ DeviceIndex: 0, Addresses: []network.ProviderAddress{ - network.NewProviderAddress("10.0.0.42", network.WithCIDR("10.0.0.0/24")), + network.NewMachineAddress("10.0.0.42", network.WithCIDR("10.0.0.0/24")).AsProviderAddress(), }, }} expectArgs := params.SetProviderNetworkConfig{ diff --git a/api/provisioner/provisioner_test.go b/api/provisioner/provisioner_test.go index f1048c9571b..8dc5e020e57 100644 --- a/api/provisioner/provisioner_test.go +++ b/api/provisioner/provisioner_test.go @@ -938,12 +938,12 @@ func (s *provisionerContainerSuite) TestPrepareContainerInterfaceInfoSingleNIC(c Disabled: false, NoAutoStart: false, ConfigType: "static", - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "192.168.0.6", corenetwork.WithCIDR("192.168.0.5/24"), corenetwork.WithConfigType(corenetwork.ConfigStatic), - )}, - DNSServers: corenetwork.NewProviderAddresses("8.8.8.8"), + ).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"8.8.8.8"}).AsProviderAddresses(), DNSSearchDomains: []string{"mydomain"}, - GatewayAddress: corenetwork.NewProviderAddress("192.168.0.1"), + GatewayAddress: corenetwork.NewMachineAddress("192.168.0.1").AsProviderAddress(), Routes: []corenetwork.Route{{ DestinationCIDR: "10.0.0.0/16", GatewayIP: "192.168.0.1", diff --git a/api/raftlease/client.go b/api/raftlease/client.go index a60f0bb6180..5693c4cef49 100644 --- a/api/raftlease/client.go +++ b/api/raftlease/client.go @@ -608,9 +608,8 @@ func (r *remote) loop() error { func (r *remote) connect() bool { stop := make(chan struct{}) - var info *api.Info r.mutex.Lock() - info = r.config.APIInfo + info := *r.config.APIInfo r.stopConnecting = stop r.mutex.Unlock() @@ -621,7 +620,7 @@ func (r *remote) connect() bool { _ = retry.Call(retry.CallArgs{ Func: func() error { r.config.Logger.Debugf("open api to %v", address) - conn, err := api.Open(info, api.DialOpts{ + conn, err := api.Open(&info, api.DialOpts{ DialAddressInterval: 50 * time.Millisecond, Timeout: 10 * time.Minute, RetryDelay: 2 * time.Second, diff --git a/api/testing/apiaddresser.go b/api/testing/apiaddresser.go index 5907774370b..d82f5f949c0 100644 --- a/api/testing/apiaddresser.go +++ b/api/testing/apiaddresser.go @@ -60,9 +60,9 @@ func (s *APIAddresserTests) TestAPIHostPorts(c *gc.C) { c.Assert(err, jc.ErrorIsNil) expectServerAddrs := []network.ProviderHostPorts{ - {network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("0.1.2.24"), NetPort: 999}}, - {network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("example.com"), NetPort: 1234}}, - {network.ProviderHostPort{ProviderAddress: network.NewProviderAddress(ipv6Addr.Value), NetPort: 999}}, + {network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("0.1.2.24").AsProviderAddress(), NetPort: 999}}, + {network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("example.com").AsProviderAddress(), NetPort: 1234}}, + {network.ProviderHostPort{ProviderAddress: network.NewMachineAddress(ipv6Addr.Value).AsProviderAddress(), NetPort: 999}}, } expectServerAddrs[2][0].Scope = network.ScopeCloudLocal diff --git a/apiserver/allfacades.go b/apiserver/allfacades.go index e7a24817afc..fb4ed4add21 100644 --- a/apiserver/allfacades.go +++ b/apiserver/allfacades.go @@ -51,7 +51,6 @@ import ( "github.com/juju/juju/apiserver/facades/client/backups" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/block" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/bundle" - "github.com/juju/juju/apiserver/facades/client/charmhub" "github.com/juju/juju/apiserver/facades/client/charms" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/client" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/cloud" // ModelUser Read @@ -151,7 +150,6 @@ func AllFacades() *facade.Registry { reg("Backups", 3, backups.NewFacadeV3) reg("Block", 2, block.NewAPI) reg("Bundle", 6, bundle.NewFacadeV6) - reg("CharmHub", 1, charmhub.NewFacade) reg("CharmDownloader", 1, charmdownloader.NewFacadeV1) reg("CharmRevisionUpdater", 2, charmrevisionupdater.NewCharmRevisionUpdaterAPI) reg("Charms", 4, charms.NewFacadeV4) diff --git a/apiserver/common/networkingcommon/networkconfigapi_test.go b/apiserver/common/networkingcommon/networkconfigapi_test.go index d5294576630..a13415a94bf 100644 --- a/apiserver/common/networkingcommon/networkconfigapi_test.go +++ b/apiserver/common/networkingcommon/networkconfigapi_test.go @@ -106,28 +106,28 @@ func (s *networkConfigSuite) TestSetObservedNetworkConfigCallsApplyOperation(c * InterfaceName: "lo", InterfaceType: "loopback", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")), + network.NewMachineAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")).AsProviderAddress(), }, // This is a quirk of the transformation. // Due to the way address type is derived, this is not equivalent // to the provider address zero-value. - GatewayAddress: network.NewProviderAddress(""), + GatewayAddress: network.NewMachineAddress("").AsProviderAddress(), }, { InterfaceName: "eth0", InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f0", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")), + network.NewMachineAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress(""), + GatewayAddress: network.NewMachineAddress("").AsProviderAddress(), }, { InterfaceName: "eth1", InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f1", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")), + network.NewMachineAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress(""), + GatewayAddress: network.NewMachineAddress("").AsProviderAddress(), }, }) } @@ -174,9 +174,9 @@ func (s *networkConfigSuite) TestSetObservedNetworkConfigFixesFanSubs(c *gc.C) { MACAddress: "aa:bb:cc:dd:ee:f0", // Gets the CIDR from the Fan segment. Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.10.10.2", network.WithCIDR("10.10.0.0/16")), + network.NewMachineAddress("10.10.10.2", network.WithCIDR("10.10.0.0/16")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress(""), + GatewayAddress: network.NewMachineAddress("").AsProviderAddress(), }, }) } @@ -270,7 +270,7 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpMultipleAddressSuccess( InterfaceName: "lo", InterfaceType: "loopback", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")), + network.NewMachineAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")).AsProviderAddress(), }, }, { // Existing device with new address. @@ -278,9 +278,9 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpMultipleAddressSuccess( InterfaceType: "ethernet", MACAddress: ethMAC, Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")), + network.NewMachineAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.10.0.1"), + GatewayAddress: network.NewMachineAddress("0.10.0.1").AsProviderAddress(), IsDefaultGateway: true, }, { // New device and addresses for eth1. @@ -288,18 +288,18 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpMultipleAddressSuccess( InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f1", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")), + network.NewMachineAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.20.0.1"), + GatewayAddress: network.NewMachineAddress("0.20.0.1").AsProviderAddress(), }, { // A duplicate is effectively ignored. InterfaceName: "eth1", InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f1", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")), + network.NewMachineAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.20.0.1"), + GatewayAddress: network.NewMachineAddress("0.20.0.1").AsProviderAddress(), }, }, false, s.state) @@ -366,9 +366,9 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpUnobservedParentNotRemo InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f1", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")), + network.NewMachineAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.20.0.1"), + GatewayAddress: network.NewMachineAddress("0.20.0.1").AsProviderAddress(), ParentInterfaceName: "eth99", Origin: network.OriginMachine, }, @@ -401,25 +401,25 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpNewSubnetsAdded(c *gc.C InterfaceName: "lo", InterfaceType: "loopback", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")), + network.NewMachineAddress("127.0.0.1", network.WithCIDR("127.0.0.0/8")).AsProviderAddress(), }, }, { InterfaceName: "eth0", InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:ff", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")), + network.NewMachineAddress("0.10.0.2", network.WithCIDR("0.10.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.10.0.1"), + GatewayAddress: network.NewMachineAddress("0.10.0.1").AsProviderAddress(), IsDefaultGateway: true, }, { InterfaceName: "eth1", InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f1", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")), + network.NewMachineAddress("0.20.0.2", network.WithCIDR("0.20.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("0.20.0.1"), + GatewayAddress: network.NewMachineAddress("0.20.0.1").AsProviderAddress(), }, }, true, s.state) @@ -499,9 +499,9 @@ func (s *networkConfigSuite) TestUpdateMachineLinkLayerOpBridgedDeviceMovesAddre InterfaceType: "bridge", MACAddress: hwAddr, Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.6", network.WithCIDR("10.0.0.0/24")), + network.NewMachineAddress("10.0.0.6", network.WithCIDR("10.0.0.0/24")).AsProviderAddress(), }, - GatewayAddress: network.NewProviderAddress("10.0.0.1"), + GatewayAddress: network.NewMachineAddress("10.0.0.1").AsProviderAddress(), Origin: network.OriginMachine, }, }, false, s.state) diff --git a/apiserver/facades/client/charmhub/charmhub.go b/apiserver/facades/client/charmhub/charmhub.go deleted file mode 100644 index 35bcea9f475..00000000000 --- a/apiserver/facades/client/charmhub/charmhub.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "context" - "time" - - "github.com/juju/errors" - "github.com/juju/loggo" - "github.com/juju/names/v4" - - "github.com/juju/charm/v9" - apiservererrors "github.com/juju/juju/apiserver/errors" - "github.com/juju/juju/apiserver/facade" - "github.com/juju/juju/apiserver/params" - "github.com/juju/juju/charmhub" - "github.com/juju/juju/charmhub/transport" - "github.com/juju/juju/environs/config" -) - -const ( - // TimeoutDuration represents how long we should wait before a response back - // from the API before timing out. - TimeoutDuration = time.Second * 30 -) - -var logger = loggo.GetLogger("juju.apiserver.charmhub") - -// Backend defines the state methods this facade needs, so they can be -// mocked for testing. -type Backend interface { - ModelConfig() (*config.Config, error) -} - -// ClientFactory defines a factory for creating clients from a given url. -type ClientFactory interface { - Client(string) (Client, error) -} - -// Client represents a CharmHub Client for making queries to the CharmHub API. -type Client interface { - URL() string - Info(ctx context.Context, name string, options ...charmhub.InfoOption) (transport.InfoResponse, error) - Find(ctx context.Context, query string, options ...charmhub.FindOption) ([]transport.FindResponse, error) -} - -// CharmHubAPI API provides the CharmHub API facade for version 1. -type CharmHubAPI struct { - auth facade.Authorizer - client Client -} - -// NewFacade creates a new CharmHubAPI facade. -func NewFacade(ctx facade.Context) (*CharmHubAPI, error) { - m, err := ctx.State().Model() - if err != nil { - return nil, errors.Trace(err) - } - - return newCharmHubAPI(m, ctx.Auth(), charmHubClientFactory{ - httpTransport: charmhub.RequestHTTPTransport(ctx.RequestRecorder(), charmhub.DefaultRetryPolicy()), - }) -} - -func newCharmHubAPI(backend Backend, authorizer facade.Authorizer, clientFactory ClientFactory) (*CharmHubAPI, error) { - if !authorizer.AuthClient() { - return nil, apiservererrors.ErrPerm - } - - modelCfg, err := backend.ModelConfig() - if err != nil { - return nil, errors.Trace(err) - } - url, _ := modelCfg.CharmHubURL() - client, err := clientFactory.Client(url) - if err != nil { - return nil, errors.Trace(err) - } - - return &CharmHubAPI{ - auth: authorizer, - client: client, - }, nil -} - -// Info queries the CharmHub API with a given entity ID. -func (api *CharmHubAPI) Info(ctx context.Context, arg params.Info) (params.CharmHubEntityInfoResult, error) { - logger.Tracef("Info(%v)", arg.Tag) - - tag, err := names.ParseApplicationTag(arg.Tag) - if err != nil { - return params.CharmHubEntityInfoResult{}, errors.BadRequestf("tag value is empty") - } - - var options []charmhub.InfoOption - if arg.Channel != "" { - ch, err := charm.ParseChannelNormalize(arg.Channel) - if err != nil { - return params.CharmHubEntityInfoResult{}, errors.BadRequestf("channel %q is invalid", arg.Channel) - } - options = append(options, charmhub.WithInfoChannel(ch.String())) - } - - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, TimeoutDuration) - defer cancel() - - info, err := api.client.Info(ctx, tag.Id(), options...) - if err != nil { - return params.CharmHubEntityInfoResult{}, errors.Trace(err) - } - result, err := convertCharmInfoResult(info) - return params.CharmHubEntityInfoResult{Result: result}, err -} - -// Find queries the CharmHub API with a given entity ID. -func (api *CharmHubAPI) Find(ctx context.Context, arg params.Query) (params.CharmHubEntityFindResult, error) { - logger.Tracef("Find(%v)", arg.Query) - - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, TimeoutDuration) - defer cancel() - - results, err := api.client.Find(ctx, arg.Query, populateFindOptions(arg)...) - if err != nil { - return params.CharmHubEntityFindResult{}, errors.Trace(err) - } - return params.CharmHubEntityFindResult{Results: convertCharmFindResults(results)}, nil -} - -type charmHubClientFactory struct { - httpTransport func(charmhub.Logger) charmhub.Transport -} - -func (f charmHubClientFactory) Client(url string) (Client, error) { - cfg, err := charmhub.CharmHubConfigFromURL(url, logger, - charmhub.WithHTTPTransport(f.httpTransport), - ) - if err != nil { - return nil, errors.Trace(err) - } - client, err := charmhub.NewClient(cfg) - if err != nil { - return nil, errors.Trace(err) - } - return client, nil -} - -func populateFindOptions(arg params.Query) []charmhub.FindOption { - var options []charmhub.FindOption - - if arg.Category != "" { - options = append(options, charmhub.WithFindCategory(arg.Category)) - } - if arg.Channel != "" { - options = append(options, charmhub.WithFindChannel(arg.Channel)) - } - if arg.CharmType != "" { - options = append(options, charmhub.WithFindType(arg.CharmType)) - } - if arg.Platforms != "" { - options = append(options, charmhub.WithFindPlatforms(arg.Platforms)) - } - if arg.Publisher != "" { - options = append(options, charmhub.WithFindPublisher(arg.Publisher)) - } - if arg.RelationRequires != "" { - options = append(options, charmhub.WithFindRelationRequires(arg.RelationRequires)) - } - if arg.RelationProvides != "" { - options = append(options, charmhub.WithFindRelationProvides(arg.RelationProvides)) - } - - return options -} diff --git a/apiserver/facades/client/charmhub/charmhub_test.go b/apiserver/facades/client/charmhub/charmhub_test.go deleted file mode 100644 index caddcf4d7cf..00000000000 --- a/apiserver/facades/client/charmhub/charmhub_test.go +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "context" - - "github.com/golang/mock/gomock" - "github.com/juju/names/v4" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - facademocks "github.com/juju/juju/apiserver/facade/mocks" - "github.com/juju/juju/apiserver/params" - "github.com/juju/juju/charmhub/transport" - "github.com/juju/juju/environs/config" - "github.com/juju/juju/testing" -) - -var _ = gc.Suite(&charmHubAPISuite{}) - -type charmHubAPISuite struct { - backend *MockBackend - authorizer *facademocks.MockAuthorizer - clientFactory *MockClientFactory - client *MockClient -} - -func (s *charmHubAPISuite) TestInfo(c *gc.C) { - defer s.setupMocks(c).Finish() - s.expectInfo() - arg := params.Info{Tag: names.NewApplicationTag("wordpress").String()} - obtained, err := s.newCharmHubAPIForTest(c).Info(context.TODO(), arg) - c.Assert(err, jc.ErrorIsNil) - - assertInfoResponseSameContents(c, obtained.Result, getParamsInfoResponse()) -} - -func (s *charmHubAPISuite) TestInfoWithChannel(c *gc.C) { - defer s.setupMocks(c).Finish() - s.expectInfo() - arg := params.Info{Tag: names.NewApplicationTag("wordpress").String(), Channel: "stable"} - obtained, err := s.newCharmHubAPIForTest(c).Info(context.TODO(), arg) - c.Assert(err, jc.ErrorIsNil) - - assertInfoResponseSameContents(c, obtained.Result, getParamsInfoResponse()) -} - -func (s *charmHubAPISuite) TestFind(c *gc.C) { - defer s.setupMocks(c).Finish() - s.expectFind() - arg := params.Query{Query: "wordpress"} - obtained, err := s.newCharmHubAPIForTest(c).Find(context.TODO(), arg) - c.Assert(err, jc.ErrorIsNil) - - c.Assert(obtained.Results, gc.HasLen, 1) - assertFindResponseSameContents(c, obtained.Results[0], getParamsFindResponse()) -} - -func (s *charmHubAPISuite) TestFindWithOptions(c *gc.C) { - defer s.setupMocks(c).Finish() - s.expectFind() - arg := params.Query{Query: "wordpress", Channel: "stable", Category: "k8s"} - obtained, err := s.newCharmHubAPIForTest(c).Find(context.TODO(), arg) - c.Assert(err, jc.ErrorIsNil) - - c.Assert(obtained.Results, gc.HasLen, 1) - assertFindResponseSameContents(c, obtained.Results[0], getParamsFindResponse()) -} - -func (s *charmHubAPISuite) newCharmHubAPIForTest(c *gc.C) *CharmHubAPI { - s.expectModelConfig(c) - s.expectAuth() - s.expectClient() - api, err := newCharmHubAPI(s.backend, s.authorizer, s.clientFactory) - c.Assert(err, jc.ErrorIsNil) - return api -} - -func (s *charmHubAPISuite) setupMocks(c *gc.C) *gomock.Controller { - ctrl := gomock.NewController(c) - s.backend = NewMockBackend(ctrl) - s.authorizer = facademocks.NewMockAuthorizer(ctrl) - s.clientFactory = NewMockClientFactory(ctrl) - s.client = NewMockClient(ctrl) - return ctrl -} - -func (s *charmHubAPISuite) expectModelConfig(c *gc.C) { - cfg, err := config.New(config.UseDefaults, map[string]interface{}{ - "charmhub-url": "https://someurl.com", - "type": "my-type", - "name": "my-name", - "uuid": testing.ModelTag.Id(), - }) - c.Assert(err, jc.ErrorIsNil) - s.backend.EXPECT().ModelConfig().Return(cfg, nil) -} - -func (s *charmHubAPISuite) expectAuth() { - s.authorizer.EXPECT().AuthClient().Return(true) -} - -func (s *charmHubAPISuite) expectClient() { - s.clientFactory.EXPECT().Client("https://someurl.com").Return(s.client, nil) -} - -func (s *charmHubAPISuite) expectInfo() { - s.client.EXPECT().Info(gomock.Any(), "wordpress", gomock.Any()).Return(getCharmHubInfoResponse(), nil) -} - -func (s *charmHubAPISuite) expectFind() { - s.client.EXPECT().Find(gomock.Any(), "wordpress", gomock.Any()).Return(getCharmHubFindResponses(), nil) -} - -func assertInfoResponseSameContents(c *gc.C, obtained, expected params.InfoResponse) { - c.Assert(obtained.Type, gc.Equals, expected.Type) - c.Assert(obtained.ID, gc.Equals, expected.ID) - c.Assert(obtained.Name, gc.Equals, expected.Name) - c.Assert(obtained.Publisher, jc.DeepEquals, expected.Publisher) - c.Assert(obtained.Summary, gc.Equals, expected.Summary) - c.Assert(obtained.Series, gc.DeepEquals, expected.Series) - c.Assert(obtained.Channels, jc.DeepEquals, expected.Channels) - c.Assert(obtained.Tracks, jc.SameContents, expected.Tracks) - c.Assert(obtained.Tags, gc.DeepEquals, expected.Tags) - c.Assert(obtained.StoreURL, gc.Equals, expected.StoreURL) - assertCharmSameContents(c, obtained.Charm, expected.Charm) -} - -func assertFindResponseSameContents(c *gc.C, obtained, expected params.FindResponse) { - c.Assert(obtained.Type, gc.Equals, expected.Type) - c.Assert(obtained.ID, gc.Equals, expected.ID) - c.Assert(obtained.Name, gc.Equals, expected.Name) - c.Assert(obtained.Publisher, gc.Equals, expected.Publisher) - c.Assert(obtained.Summary, gc.Equals, expected.Summary) - c.Assert(obtained.Version, gc.Equals, expected.Version) - c.Assert(obtained.StoreURL, gc.Equals, expected.StoreURL) - c.Assert(obtained.Series, gc.DeepEquals, expected.Series) -} - -func assertCharmSameContents(c *gc.C, obtained, expected *params.CharmHubCharm) { - c.Assert(obtained.Config, gc.DeepEquals, expected.Config) - c.Assert(obtained.Relations, jc.DeepEquals, expected.Relations) - c.Assert(obtained.Subordinate, gc.Equals, expected.Subordinate) - c.Assert(obtained.UsedBy, gc.DeepEquals, expected.UsedBy) -} - -func getCharmHubFindResponses() []transport.FindResponse { - return []transport.FindResponse{{ - Name: "wordpress", - Type: "object", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Entity: transport.Entity{ - Categories: []transport.Category{{ - Featured: true, - Name: "blog", - }}, - License: "Apache-2.0", - Description: "This will install and setup Wordpress optimized to run in the cloud.\nBy default it will place Ngnix configured to scale horizontally\nwith Nginx's reverse proxy.", - Publisher: map[string]string{ - "display-name": "Wordress Charmers", - }, - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - StoreURL: "https://someurl.com/wordpress", - UsedBy: []string{ - "wordpress-everlast", - "wordpress-jorge", - "wordpress-site", - }, - }, - DefaultRelease: transport.FindChannelMap{ - Channel: transport.Channel{ - Name: "latest/stable", - Base: transport.Base{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }, - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Risk: "stable", - Track: "latest", - }, - Revision: transport.FindRevision{ - CreatedAt: "2019-12-16T19:20:26.673192+00:00", - Download: transport.Download{ - HashSHA256: "92a8b825ed1108ab64864a7df05eb84ed3925a8d5e4741169185f77cef9b52517ad4b79396bab43b19e544a908ec83c4", - Size: 12042240, - URL: "https://api.snapcraft.io/api/v1/snaps/download/QLLfVfIKfcnTZiPFnmGcigB2vB605ZY7_16.snap", - }, - Bases: []transport.Base{{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }, { - Architecture: "all", - Name: "ubuntu", - Channel: "16.04", - }}, - Revision: 16, - Version: "1.0.3", - }, - }, - }} -} - -func getCharmHubInfoResponse() transport.InfoResponse { - return transport.InfoResponse{ - Name: "wordpress", - Type: "charm", - ID: "charmCHARMcharmCHARMcharmCHARM01", - ChannelMap: []transport.InfoChannelMap{{ - Channel: transport.Channel{ - Name: "latest/stable", - Base: transport.Base{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }, - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Risk: "stable", - Track: "latest", - }, - Revision: transport.InfoRevision{ - ConfigYAML: "one: 1\ntwo: 2\nitems: [1,2,3,4]\n\"", - CreatedAt: "2019-12-16T19:20:26.673192+00:00", - Download: transport.Download{ - HashSHA256: "92a8b825ed1108ab64864a7df05eb84ed3925a8d5e4741169185f77cef9b52517ad4b79396bab43b19e544a908ec83c4", - Size: 12042240, - URL: "https://api.snapcraft.io/api/v1/snaps/download/QLLfVfIKfcnTZiPFnmGcigB2vB605ZY7_16.snap", - }, - MetadataYAML: "name: myname\nversion: 1.0.3\nsummary: A charm or bundle.\ndescription: |\n This will install and setup services optimized to run in the cloud.\n By default it will place Ngnix configured to scale horizontally\n with Nginx's reverse proxy.\n", - Bases: []transport.Base{{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }}, - Revision: 16, - Version: "1.0.3", - }, - }}, - Entity: transport.Entity{ - Categories: []transport.Category{{ - Featured: true, - Name: "blog", - }}, - License: "Apache-2.0", - Description: "This will install and setup Wordpress optimized to run in the cloud.\nBy default it will place Ngnix configured to scale horizontally\nwith Nginx's reverse proxy.", - Publisher: map[string]string{ - "display-name": "Wordress Charmers", - }, - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - StoreURL: "https://someurl.com/wordpress", - UsedBy: []string{ - "wordpress-everlast", - "wordpress-jorge", - "wordpress-site", - }, - }, - DefaultRelease: transport.InfoChannelMap{ - Channel: transport.Channel{ - Name: "latest/stable", - Base: transport.Base{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }, - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Risk: "stable", - Track: "latest", - }, - Revision: transport.InfoRevision{ - ConfigYAML: entityConfig, - CreatedAt: "2019-12-16T19:20:26.673192+00:00", - Download: transport.Download{ - HashSHA256: "92a8b825ed1108ab64864a7df05eb84ed3925a8d5e4741169185f77cef9b52517ad4b79396bab43b19e544a908ec83c4", - Size: 12042240, - URL: "https://api.snapcraft.io/api/v1/snaps/download/QLLfVfIKfcnTZiPFnmGcigB2vB605ZY7_16.snap", - }, - MetadataYAML: entityMeta, - Bases: []transport.Base{{ - Architecture: "all", - Name: "ubuntu", - Channel: "18.04", - }}, - Revision: 16, - Version: "1.0.3", - }, - }, - } -} - -func getParamsInfoResponse() params.InfoResponse { - return params.InfoResponse{ - Name: "wordpress", - Type: "charm", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Description: "This will install and setup WordPress optimized to run in the cloud.\nBy default it will place Ngnix configured to scale horizontally\nwith Nginx's reverse proxy.", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Tracks: []string{"latest"}, - Series: []string{"bionic", "xenial"}, - StoreURL: "https://someurl.com/wordpress", - Tags: []string{"blog"}, - Channels: map[string]params.Channel{ - "latest/stable": { - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Track: "latest", - Risk: "stable", - Size: 12042240, - Revision: 16, - Version: "1.0.3", - Platforms: []params.Platform{{Architecture: "all", OS: "ubuntu", Series: "bionic"}}, - }}, - Charm: ¶ms.CharmHubCharm{ - Subordinate: false, - Config: map[string]params.CharmOption{ - "reticulate-splines": {Type: "boolean", Description: "Whether to reticulate splines on launch, or not."}, - "title": {Type: "string", Description: "A descriptive title used for the application.", Default: "My Title"}, - "subtitle": {Type: "string", Description: "An optional subtitle used for the application.", Default: ""}, - "outlook": {Type: "string", Description: "No default outlook."}, - "username": {Type: "string", Description: "The name of the initial account (given admin permissions).", Default: "admin001"}, - "skill-level": {Type: "int", Description: "A number indicating skill."}, - "agility-ratio": {Type: "float", Description: "A number from 0 to 1 indicating agility."}, - }, - Relations: map[string]map[string]string{ - "provides": {"source": "dummy-token"}, - "requires": {"sink": "dummy-token"}}, - UsedBy: []string{ - "wordpress-everlast", - "wordpress-jorge", - "wordpress-site", - }, - }, - } -} - -func getParamsFindResponse() params.FindResponse { - return params.FindResponse{ - Type: "object", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Name: "wordpress", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Version: "1.0.3", - Series: []string{"bionic", "xenial"}, - StoreURL: "https://someurl.com/wordpress", - } -} - -var entityMeta = ` -name: myname -version: 1.0.3 -subordinate: false -summary: A charm or bundle. -description: | - This will install and setup services optimized to run in the cloud. - By default it will place Ngnix configured to scale horizontally - with Nginx's reverse proxy. -series: [bionic, xenial] -provides: - source: - interface: dummy-token -requires: - sink: - interface: dummy-token -` - -var entityConfig = ` -options: - title: - default: My Title - description: A descriptive title used for the application. - type: string - subtitle: - default: "" - description: An optional subtitle used for the application. - outlook: - description: No default outlook. - # type defaults to string in python - username: - default: admin001 - description: The name of the initial account (given admin permissions). - type: string - skill-level: - description: A number indicating skill. - type: int - agility-ratio: - description: A number from 0 to 1 indicating agility. - type: float - reticulate-splines: - description: Whether to reticulate splines on launch, or not. - type: boolean -` diff --git a/apiserver/facades/client/charmhub/package_mock_test.go b/apiserver/facades/client/charmhub/package_mock_test.go deleted file mode 100644 index 6fcae50adad..00000000000 --- a/apiserver/facades/client/charmhub/package_mock_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/juju/juju/apiserver/facades/client/charmhub (interfaces: Backend,ClientFactory,Client) - -// Package charmhub is a generated GoMock package. -package charmhub - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - charmhub0 "github.com/juju/juju/charmhub" - transport "github.com/juju/juju/charmhub/transport" - config "github.com/juju/juju/environs/config" -) - -// MockBackend is a mock of Backend interface. -type MockBackend struct { - ctrl *gomock.Controller - recorder *MockBackendMockRecorder -} - -// MockBackendMockRecorder is the mock recorder for MockBackend. -type MockBackendMockRecorder struct { - mock *MockBackend -} - -// NewMockBackend creates a new mock instance. -func NewMockBackend(ctrl *gomock.Controller) *MockBackend { - mock := &MockBackend{ctrl: ctrl} - mock.recorder = &MockBackendMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBackend) EXPECT() *MockBackendMockRecorder { - return m.recorder -} - -// ModelConfig mocks base method. -func (m *MockBackend) ModelConfig() (*config.Config, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModelConfig") - ret0, _ := ret[0].(*config.Config) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ModelConfig indicates an expected call of ModelConfig. -func (mr *MockBackendMockRecorder) ModelConfig() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModelConfig", reflect.TypeOf((*MockBackend)(nil).ModelConfig)) -} - -// MockClientFactory is a mock of ClientFactory interface. -type MockClientFactory struct { - ctrl *gomock.Controller - recorder *MockClientFactoryMockRecorder -} - -// MockClientFactoryMockRecorder is the mock recorder for MockClientFactory. -type MockClientFactoryMockRecorder struct { - mock *MockClientFactory -} - -// NewMockClientFactory creates a new mock instance. -func NewMockClientFactory(ctrl *gomock.Controller) *MockClientFactory { - mock := &MockClientFactory{ctrl: ctrl} - mock.recorder = &MockClientFactoryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClientFactory) EXPECT() *MockClientFactoryMockRecorder { - return m.recorder -} - -// Client mocks base method. -func (m *MockClientFactory) Client(arg0 string) (Client, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Client", arg0) - ret0, _ := ret[0].(Client) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Client indicates an expected call of Client. -func (mr *MockClientFactoryMockRecorder) Client(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Client", reflect.TypeOf((*MockClientFactory)(nil).Client), arg0) -} - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockClient) Find(arg0 context.Context, arg1 string, arg2 ...charmhub0.FindOption) ([]transport.FindResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Find", varargs...) - ret0, _ := ret[0].([]transport.FindResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockClientMockRecorder) Find(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockClient)(nil).Find), varargs...) -} - -// Info mocks base method. -func (m *MockClient) Info(arg0 context.Context, arg1 string, arg2 ...charmhub0.InfoOption) (transport.InfoResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Info", varargs...) - ret0, _ := ret[0].(transport.InfoResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Info indicates an expected call of Info. -func (mr *MockClientMockRecorder) Info(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockClient)(nil).Info), varargs...) -} - -// URL mocks base method. -func (m *MockClient) URL() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "URL") - ret0, _ := ret[0].(string) - return ret0 -} - -// URL indicates an expected call of URL. -func (mr *MockClientMockRecorder) URL() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "URL", reflect.TypeOf((*MockClient)(nil).URL)) -} diff --git a/apiserver/facades/client/charmhub/package_test.go b/apiserver/facades/client/charmhub/package_test.go deleted file mode 100644 index ff30d71ed78..00000000000 --- a/apiserver/facades/client/charmhub/package_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "testing" - - gc "gopkg.in/check.v1" -) - -//go:generate go run github.com/golang/mock/mockgen -package charmhub -destination package_mock_test.go github.com/juju/juju/apiserver/facades/client/charmhub Backend,ClientFactory,Client - -func TestPackage(t *testing.T) { - gc.TestingT(t) -} diff --git a/apiserver/facades/client/client/client_test.go b/apiserver/facades/client/client/client_test.go index c15f1a70598..e7714bf4861 100644 --- a/apiserver/facades/client/client/client_test.go +++ b/apiserver/facades/client/client/client_test.go @@ -1451,7 +1451,7 @@ func (s *clientSuite) TestClientAddMachinesSomeErrors(c *gc.C) { func (s *clientSuite) TestClientAddMachinesWithInstanceIdSomeErrors(c *gc.C) { apiParams := make([]params.AddMachineParams, 3) - addrs := network.NewProviderAddresses("1.2.3.4") + addrs := network.NewMachineAddresses([]string{"1.2.3.4"}).AsProviderAddresses() hc := instance.MustParseHardware("mem=4G") for i := 0; i < 3; i++ { apiParams[i] = params.AddMachineParams{ diff --git a/apiserver/facades/client/client/instanceconfig_test.go b/apiserver/facades/client/client/instanceconfig_test.go index 03eb58b98be..cda010bcc80 100644 --- a/apiserver/facades/client/client/instanceconfig_test.go +++ b/apiserver/facades/client/client/instanceconfig_test.go @@ -36,7 +36,7 @@ func (s *machineConfigSuite) TestMachineConfig(c *gc.C) { InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: hc, - Addrs: params.FromProviderAddresses(network.NewProviderAddress("1.2.3.4")), + Addrs: params.FromProviderAddresses(network.NewMachineAddress("1.2.3.4").AsProviderAddress()), } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, jc.ErrorIsNil) @@ -80,7 +80,7 @@ func (s *machineConfigSuite) TestMachineConfigNoTools(c *gc.C) { InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: hc, - Addrs: params.FromProviderAddresses(network.NewProviderAddress("1.2.3.4")), + Addrs: params.FromProviderAddresses(network.NewMachineAddress("1.2.3.4").AsProviderAddress()), } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, jc.ErrorIsNil) diff --git a/apiserver/facades/controller/instancepoller/instancepoller.go b/apiserver/facades/controller/instancepoller/instancepoller.go index 98e12cdb109..02d58b08ff7 100644 --- a/apiserver/facades/controller/instancepoller/instancepoller.go +++ b/apiserver/facades/controller/instancepoller/instancepoller.go @@ -229,7 +229,10 @@ func mapNetworkConfigsToProviderAddresses( addrs = append( addrs, - network.NewProviderAddressInSpace(string(spaceInfo.Name), addr.Value, network.WithScope(addr.Scope)), + network.NewMachineAddress( + addr.Value, + network.WithScope(addr.Scope), + ).AsProviderAddress(network.WithSpaceName(string(spaceInfo.Name))), ) } @@ -249,7 +252,10 @@ func mapNetworkConfigsToProviderAddresses( } addrs = append( addrs, - network.NewProviderAddressInSpace(string(spaceInfo.Name), addr.Value, network.WithScope(addr.Scope)), + network.NewMachineAddress( + addr.Value, + network.WithScope(addr.Scope), + ).AsProviderAddress(network.WithSpaceName(string(spaceInfo.Name))), ) } } diff --git a/apiserver/facades/schema.json b/apiserver/facades/schema.json index 42e8b0f89cc..6052e7ed780 100644 --- a/apiserver/facades/schema.json +++ b/apiserver/facades/schema.json @@ -14209,436 +14209,6 @@ } } }, - { - "Name": "CharmHub", - "Description": "CharmHubAPI API provides the CharmHub API facade for version 1.", - "Version": 1, - "AvailableTo": [ - "controller-machine-agent", - "machine-agent", - "unit-agent", - "model-user" - ], - "Schema": { - "type": "object", - "properties": { - "Find": { - "type": "object", - "properties": { - "Params": { - "$ref": "#/definitions/Query" - }, - "Result": { - "$ref": "#/definitions/CharmHubEntityFindResult" - } - }, - "description": "Find queries the CharmHub API with a given entity ID." - }, - "Info": { - "type": "object", - "properties": { - "Params": { - "$ref": "#/definitions/Info" - }, - "Result": { - "$ref": "#/definitions/CharmHubEntityInfoResult" - } - }, - "description": "Info queries the CharmHub API with a given entity ID." - } - }, - "definitions": { - "BundleCharm": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "package-id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "name", - "package-id" - ] - }, - "Channel": { - "type": "object", - "properties": { - "platforms": { - "type": "array", - "items": { - "$ref": "#/definitions/Platform" - } - }, - "released-at": { - "type": "string" - }, - "revision": { - "type": "integer" - }, - "risk": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "track": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "released-at", - "track", - "risk", - "revision", - "size", - "version", - "platforms" - ] - }, - "CharmHubBundle": { - "type": "object", - "properties": { - "charms": { - "type": "array", - "items": { - "$ref": "#/definitions/BundleCharm" - } - } - }, - "additionalProperties": false, - "required": [ - "charms" - ] - }, - "CharmHubCharm": { - "type": "object", - "properties": { - "config": { - "type": "object", - "patternProperties": { - ".*": { - "$ref": "#/definitions/CharmOption" - } - } - }, - "relations": { - "type": "object", - "patternProperties": { - ".*": { - "type": "object", - "patternProperties": { - ".*": { - "type": "string" - } - } - } - } - }, - "subordinate": { - "type": "boolean" - }, - "used-by": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": [ - "config", - "relations", - "subordinate", - "used-by" - ] - }, - "CharmHubEntityFindResult": { - "type": "object", - "properties": { - "errors": { - "$ref": "#/definitions/ErrorResponse" - }, - "result": { - "type": "array", - "items": { - "$ref": "#/definitions/FindResponse" - } - } - }, - "additionalProperties": false, - "required": [ - "result", - "errors" - ] - }, - "CharmHubEntityInfoResult": { - "type": "object", - "properties": { - "errors": { - "$ref": "#/definitions/ErrorResponse" - }, - "result": { - "$ref": "#/definitions/InfoResponse" - } - }, - "additionalProperties": false, - "required": [ - "result", - "errors" - ] - }, - "CharmHubError": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "code", - "message" - ] - }, - "CharmOption": { - "type": "object", - "properties": { - "default": { - "type": "object", - "additionalProperties": true - }, - "description": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "error-list": { - "$ref": "#/definitions/CharmHubError" - } - }, - "additionalProperties": false, - "required": [ - "error-list" - ] - }, - "FindResponse": { - "type": "object", - "properties": { - "architectures": { - "type": "array", - "items": { - "type": "string" - } - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "os": { - "type": "array", - "items": { - "type": "string" - } - }, - "publisher": { - "type": "string" - }, - "series": { - "type": "array", - "items": { - "type": "string" - } - }, - "store-url": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "type": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type", - "id", - "name", - "publisher", - "summary", - "version", - "store-url" - ] - }, - "Info": { - "type": "object", - "properties": { - "channel": { - "type": "string" - }, - "tag": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "tag" - ] - }, - "InfoResponse": { - "type": "object", - "properties": { - "bundle": { - "$ref": "#/definitions/CharmHubBundle" - }, - "channel-map": { - "type": "object", - "patternProperties": { - ".*": { - "$ref": "#/definitions/Channel" - } - } - }, - "charm": { - "$ref": "#/definitions/CharmHubCharm" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "series": { - "type": "array", - "items": { - "type": "string" - } - }, - "store-url": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "tracks": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type", - "id", - "name", - "description", - "publisher", - "summary", - "series", - "store-url", - "tags", - "channel-map", - "tracks" - ] - }, - "Platform": { - "type": "object", - "properties": { - "architecture": { - "type": "string" - }, - "os": { - "type": "string" - }, - "series": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "architecture", - "os", - "series" - ] - }, - "Query": { - "type": "object", - "properties": { - "category": { - "type": "string" - }, - "channel": { - "type": "string" - }, - "platforms": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "query": { - "type": "string" - }, - "relation-provides": { - "type": "string" - }, - "relation-requires": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "query" - ] - } - } - } - }, { "Name": "CharmRevisionUpdater", "Description": "CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete\nimplementation of the api end point.", diff --git a/apiserver/params/network.go b/apiserver/params/network.go index 40bdedff8ea..f5b7ab89ba4 100644 --- a/apiserver/params/network.go +++ b/apiserver/params/network.go @@ -302,9 +302,9 @@ func InterfaceInfoFromNetworkConfig(configs []NetworkConfig) network.InterfaceIn ConfigType: configType, Addresses: ToProviderAddresses(v.Addresses...), ShadowAddresses: ToProviderAddresses(v.ShadowAddresses...), - DNSServers: network.NewProviderAddresses(v.DNSServers...), + DNSServers: network.NewMachineAddresses(v.DNSServers).AsProviderAddresses(), DNSSearchDomains: v.DNSSearchDomains, - GatewayAddress: network.NewProviderAddress(v.GatewayAddress), + GatewayAddress: network.NewMachineAddress(v.GatewayAddress).AsProviderAddress(), Routes: routes, IsDefaultGateway: v.IsDefaultGateway, VirtualPortType: network.VirtualPortType(v.VirtualPortType), @@ -334,11 +334,11 @@ func InterfaceInfoFromNetworkConfig(configs []NetworkConfig) network.InterfaceIn // 2) For even older clients that do not populate Addresses. if v.Address != "" { result[i].Addresses = network.ProviderAddresses{ - network.NewProviderAddress( + network.NewMachineAddress( v.Address, network.WithCIDR(v.CIDR), network.WithConfigType(configType), - ), + ).AsProviderAddress(), } } } diff --git a/apiserver/params/network_test.go b/apiserver/params/network_test.go index 779c7fcdcae..4db10852266 100644 --- a/apiserver/params/network_test.go +++ b/apiserver/params/network_test.go @@ -260,9 +260,9 @@ func (s *NetworkSuite) TestPortRangeConvenience(c *gc.C) { func (s *NetworkSuite) TestProviderAddressConversion(c *gc.C) { pAddrs := network.ProviderAddresses{ - network.NewProviderAddress("1.2.3.4", network.WithScope(network.ScopeCloudLocal), network.WithCIDR("1.2.3.0/24")), - network.NewProviderAddress("1.2.3.5", network.WithScope(network.ScopeCloudLocal), network.WithSecondary(true)), - network.NewProviderAddress("2.3.4.5", network.WithScope(network.ScopePublic), network.WithConfigType("dhcp")), + network.NewMachineAddress("1.2.3.4", network.WithScope(network.ScopeCloudLocal), network.WithCIDR("1.2.3.0/24")).AsProviderAddress(), + network.NewMachineAddress("1.2.3.5", network.WithScope(network.ScopeCloudLocal), network.WithSecondary(true)).AsProviderAddress(), + network.NewMachineAddress("2.3.4.5", network.WithScope(network.ScopePublic), network.WithConfigType("dhcp")).AsProviderAddress(), } pAddrs[0].SpaceName = "test-space" pAddrs[0].ProviderSpaceID = "666" @@ -290,17 +290,17 @@ func (s *NetworkSuite) TestProviderHostPortConversion(c *gc.C) { pHPs := []network.ProviderHostPorts{ { { - ProviderAddress: network.NewProviderAddress("1.2.3.4", network.WithScope(network.ScopeCloudLocal)), + ProviderAddress: network.NewMachineAddress("1.2.3.4", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), NetPort: 1234, }, { - ProviderAddress: network.NewProviderAddress("2.3.4.5", network.WithScope(network.ScopePublic)), + ProviderAddress: network.NewMachineAddress("2.3.4.5", network.WithScope(network.ScopePublic)).AsProviderAddress(), NetPort: 2345, }, }, { { - ProviderAddress: network.NewProviderAddress("3.4.5.6", network.WithScope(network.ScopeCloudLocal)), + ProviderAddress: network.NewMachineAddress("3.4.5.6", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), NetPort: 3456, }, }, diff --git a/caas/kubernetes/provider/k8s_test.go b/caas/kubernetes/provider/k8s_test.go index b20d9c0285e..372657f5dfb 100644 --- a/caas/kubernetes/provider/k8s_test.go +++ b/caas/kubernetes/provider/k8s_test.go @@ -3218,8 +3218,8 @@ func (s *K8sBrokerSuite) TestGetServiceSvcFoundNoWorkload(c *gc.C) { &caas.Service{ Id: "uid-xxxxx", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopePublic)), - network.NewProviderAddress("host.com.au", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("host.com.au", network.WithScope(network.ScopePublic)).AsProviderAddress(), }, }, s.mockStatefulSets.EXPECT().Get(gomock.Any(), "app-name", v1.GetOptions{}). @@ -3309,8 +3309,8 @@ func (s *K8sBrokerSuite) assertGetServiceSvcFoundWithStatefulSet(c *gc.C, mode c &caas.Service{ Id: "uid-xxxxx", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopePublic)), - network.NewProviderAddress("host.com.au", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("host.com.au", network.WithScope(network.ScopePublic)).AsProviderAddress(), }, Scale: k8sutils.IntPtr(2), Generation: pointer.Int64Ptr(1), @@ -3401,8 +3401,8 @@ func (s *K8sBrokerSuite) assertGetServiceSvcFoundWithDeployment(c *gc.C, mode ca &caas.Service{ Id: "uid-xxxxx", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopePublic)), - network.NewProviderAddress("host.com.au", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("host.com.au", network.WithScope(network.ScopePublic)).AsProviderAddress(), }, Scale: k8sutils.IntPtr(2), Generation: pointer.Int64Ptr(1), @@ -3465,8 +3465,8 @@ func (s *K8sBrokerSuite) TestGetServiceSvcFoundWithDaemonSet(c *gc.C) { &caas.Service{ Id: "uid-xxxxx", Addresses: network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopePublic)), - network.NewProviderAddress("host.com.au", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("host.com.au", network.WithScope(network.ScopePublic)).AsProviderAddress(), }, Scale: k8sutils.IntPtr(2), Generation: pointer.Int64Ptr(1), diff --git a/caas/kubernetes/provider/utils/address.go b/caas/kubernetes/provider/utils/address.go index 1f0c8a28ba0..0a5e40efe54 100644 --- a/caas/kubernetes/provider/utils/address.go +++ b/caas/kubernetes/provider/utils/address.go @@ -24,7 +24,7 @@ func GetSvcAddresses(svc *core.Service, includeClusterIP bool) []network.Provide appendUniqueAddrs := func(scope network.Scope, addrs ...string) { for _, v := range addrs { if v != "" && v != "None" && !addressExist(v) { - netAddrs = append(netAddrs, network.NewProviderAddress(v, network.WithScope(scope))) + netAddrs = append(netAddrs, network.NewMachineAddress(v, network.WithScope(scope)).AsProviderAddress()) } } } diff --git a/charmhub/http.go b/charmhub/http.go index ac50a73c964..a71f037ecfe 100644 --- a/charmhub/http.go +++ b/charmhub/http.go @@ -90,7 +90,7 @@ func (r loggingRequestRecorder) Record(method string, url *url.URL, res *http.Re } } -// Record an outgoing request which returned back an error. +// RecordError records an outgoing request which returned an error. func (r loggingRequestRecorder) RecordError(method string, url *url.URL, err error) { if r.logger.IsTraceEnabled() { r.logger.Tracef("request error (method: %q, host: %q, path: %q, err: %s)", method, url.Host, url.Path, err) diff --git a/cloudconfig/cloudinit/network_ubuntu_test.go b/cloudconfig/cloudinit/network_ubuntu_test.go index 745df8a80ef..2e495facbc0 100644 --- a/cloudconfig/cloudinit/network_ubuntu_test.go +++ b/cloudconfig/cloudinit/network_ubuntu_test.go @@ -68,10 +68,10 @@ func (s *NetworkUbuntuSuite) SetUpTest(c *gc.C) { ConfigType: corenetwork.ConfigStatic, NoAutoStart: false, Addresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("0.1.2.3", corenetwork.WithCIDR("0.1.2.0/24"))}, - DNSServers: corenetwork.NewProviderAddresses("ns1.invalid", "ns2.invalid"), + corenetwork.NewMachineAddress("0.1.2.3", corenetwork.WithCIDR("0.1.2.0/24")).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.invalid", "ns2.invalid"}).AsProviderAddresses(), DNSSearchDomains: []string{"foo", "bar"}, - GatewayAddress: corenetwork.NewProviderAddress("0.1.2.1"), + GatewayAddress: corenetwork.NewMachineAddress("0.1.2.1").AsProviderAddress(), MACAddress: "aa:bb:cc:dd:ee:f0", MTU: 8317, }, { @@ -79,10 +79,10 @@ func (s *NetworkUbuntuSuite) SetUpTest(c *gc.C) { ConfigType: corenetwork.ConfigStatic, NoAutoStart: false, Addresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("0.2.2.4", corenetwork.WithCIDR("0.2.2.0/24"))}, - DNSServers: corenetwork.NewProviderAddresses("ns1.invalid", "ns2.invalid"), + corenetwork.NewMachineAddress("0.2.2.4", corenetwork.WithCIDR("0.2.2.0/24")).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.invalid", "ns2.invalid"}).AsProviderAddresses(), DNSSearchDomains: []string{"foo", "bar"}, - GatewayAddress: corenetwork.NewProviderAddress("0.2.2.1"), + GatewayAddress: corenetwork.NewMachineAddress("0.2.2.1").AsProviderAddress(), MACAddress: "aa:bb:cc:dd:ee:f1", Routes: []corenetwork.Route{{ DestinationCIDR: "0.5.6.0/24", @@ -110,8 +110,8 @@ func (s *NetworkUbuntuSuite) SetUpTest(c *gc.C) { MACAddress: "aa:bb:cc:dd:ee:f5", NoAutoStart: false, Addresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("2001:db8::dead:beef", corenetwork.WithCIDR("2001:db8::/64"))}, - GatewayAddress: corenetwork.NewProviderAddress("2001:db8::dead:f00"), + corenetwork.NewMachineAddress("2001:db8::dead:beef", corenetwork.WithCIDR("2001:db8::/64")).AsProviderAddress()}, + GatewayAddress: corenetwork.NewMachineAddress("2001:db8::dead:f00").AsProviderAddress(), }} for _, version := range []string{ diff --git a/cmd/juju/backups/create.go b/cmd/juju/backups/create.go index 4b5084978da..c6877de4aaf 100644 --- a/cmd/juju/backups/create.go +++ b/cmd/juju/backups/create.go @@ -45,7 +45,6 @@ Examples: juju create-backup --no-download See also: - backups download-backup ` diff --git a/cmd/juju/charmhub/charmhub.go b/cmd/juju/charmhub/charmhub.go index 15bf6feeefe..66edd3a231a 100644 --- a/cmd/juju/charmhub/charmhub.go +++ b/cmd/juju/charmhub/charmhub.go @@ -5,55 +5,45 @@ package charmhub import ( "fmt" + "net/url" + "os" "strings" "github.com/juju/cmd/v3" "github.com/juju/errors" "github.com/juju/gnuflag" + "github.com/juju/loggo" - "github.com/juju/juju/api/base" - "github.com/juju/juju/api/modelconfig" + "github.com/juju/juju/charmhub" "github.com/juju/juju/cmd/modelcmd" "github.com/juju/juju/core/arch" - "github.com/juju/juju/environs/config" ) -// ModelConfigClient represents a model config client for requesting model -// configurations. -type ModelConfigClient interface { - ModelConfigGetter - Close() error -} +var logger = loggo.GetLogger("juju.cmd.juju.charmhub") func newCharmHubCommand() *charmHubCommand { - cmd := &charmHubCommand{ + return &charmHubCommand{ arches: arch.AllArches(), + CharmHubClientFunc: func(config charmhub.Config, fs charmhub.FileSystem) (CharmHubClient, error) { + return charmhub.NewClientWithFileSystem(config, fs) + }, } - cmd.APIRootFunc = func() (base.APICallCloser, error) { - return cmd.NewAPIRoot() - } - cmd.ModelConfigClientFunc = func(api base.APICallCloser) ModelConfigClient { - return modelconfig.NewClient(api) - } - return cmd } type charmHubCommand struct { - modelcmd.ModelCommandBase + cmd.CommandBase + modelcmd.FilesystemCommand - APIRootFunc func() (base.APICallCloser, error) - ModelConfigClientFunc func(base.APICallCloser) ModelConfigClient + arch string + arches arch.Arches + series string + charmHubURL string - arch string - arches arch.Arches - series string + CharmHubClientFunc func(charmhub.Config, charmhub.FileSystem) (CharmHubClient, error) } func (c *charmHubCommand) SetFlags(f *gnuflag.FlagSet) { - c.ModelCommandBase.SetFlags(f) - - f.StringVar(&c.arch, "arch", ArchAll, fmt.Sprintf("specify an arch <%s>", c.archArgumentList())) - f.StringVar(&c.series, "series", SeriesAll, "specify a series") + f.StringVar(&c.charmHubURL, "charmhub-url", charmhub.CharmHubServerURL, "specify the Charmhub URL for querying the store") } // Init initializes the info command, including validating the provided @@ -69,42 +59,17 @@ func (c *charmHubCommand) Init(args []string) error { return errors.Errorf("unexpected architecture flag value %q, expected <%s>", c.arch, c.archArgumentList()) } - return nil -} - -func (c *charmHubCommand) Run(ctx *cmd.Context) error { - apiRoot, err := c.APIRootFunc() - if err != nil { - return errors.Trace(err) - } - defer func() { _ = apiRoot.Close() }() - - modelConfigClient := c.ModelConfigClientFunc(apiRoot) - defer func() { _ = modelConfigClient.Close() }() - - if err := c.verifySeries(modelConfigClient); err != nil { - return errors.Trace(err) - } - - return nil -} - -func (c *charmHubCommand) verifySeries(modelConfigClient ModelConfigGetter) error { - if c.series != "" { - return nil + if urlFromEnv := os.Getenv("CHARMHUB_URL"); urlFromEnv != "" { + c.charmHubURL = urlFromEnv } - - attrs, err := modelConfigClient.ModelGet() - if err != nil { - return errors.Trace(err) + if c.charmHubURL == "" { + c.charmHubURL = charmhub.CharmHubServerURL } - cfg, err := config.New(config.NoDefaults, attrs) + _, err := url.ParseRequestURI(c.charmHubURL) if err != nil { - return errors.Trace(err) - } - if defaultSeries, explicit := cfg.DefaultSeries(); explicit { - c.series = defaultSeries + return errors.Annotatef(err, "invalid charmhub-url") } + return nil } diff --git a/apiserver/facades/client/charmhub/convert.go b/cmd/juju/charmhub/convert.go similarity index 66% rename from apiserver/facades/client/charmhub/convert.go rename to cmd/juju/charmhub/convert.go index 58fe10b2117..a063cb5e632 100644 --- a/apiserver/facades/client/charmhub/convert.go +++ b/cmd/juju/charmhub/convert.go @@ -1,4 +1,4 @@ -// Copyright 2020 Canonical Ltd. +// Copyright 2022 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmhub @@ -11,14 +11,14 @@ import ( "github.com/juju/collections/set" "github.com/juju/errors" - "github.com/juju/juju/apiserver/params" "github.com/juju/juju/charmhub/transport" + "github.com/juju/juju/core/arch" corecharm "github.com/juju/juju/core/charm" coreseries "github.com/juju/juju/core/series" ) -func convertCharmInfoResult(info transport.InfoResponse) (params.InfoResponse, error) { - ir := params.InfoResponse{ +func convertCharmInfoResult(info transport.InfoResponse, arch, series string) (InfoResponse, error) { + ir := InfoResponse{ Type: string(info.Type), ID: info.ID, Name: info.Name, @@ -28,7 +28,6 @@ func convertCharmInfoResult(info transport.InfoResponse) (params.InfoResponse, e Tags: categories(info.Entity.Categories), StoreURL: info.Entity.StoreURL, } - var isKub bool switch transport.Type(ir.Type) { case transport.BundleType: ir.Bundle = convertBundle(info.Entity.Charms) @@ -36,37 +35,26 @@ func convertCharmInfoResult(info transport.InfoResponse) (params.InfoResponse, e // InfoResponse at a high level. case transport.CharmType: var err error - ir.Charm, ir.Series, isKub, err = convertCharm(info) + ir.Charm, ir.Series, err = convertCharm(info) if err != nil { - return params.InfoResponse{}, errors.Trace(err) + return InfoResponse{}, errors.Trace(err) } } - ir.Tracks, ir.Channels = transformInfoChannelMap(info.ChannelMap, isKub) + ir.Tracks, ir.Channels = filterChannels(info.ChannelMap, isKubernetes(ir.Series), arch, series) return ir, nil } -func categories(cats []transport.Category) []string { - if len(cats) == 0 { - return nil - } - result := make([]string, len(cats)) - for i, v := range cats { - result[i] = v.Name - } - return result -} - -func convertCharmFindResults(responses []transport.FindResponse) []params.FindResponse { - results := make([]params.FindResponse, len(responses)) - for k, response := range responses { - results[k] = convertCharmFindResult(response) +func convertCharmFindResults(responses []transport.FindResponse) []FindResponse { + results := make([]FindResponse, len(responses)) + for i, resp := range responses { + results[i] = convertCharmFindResult(resp) } return results } -func convertCharmFindResult(resp transport.FindResponse) params.FindResponse { - result := params.FindResponse{ +func convertCharmFindResult(resp transport.FindResponse) FindResponse { + result := FindResponse{ Type: string(resp.Type), ID: resp.ID, Name: resp.Name, @@ -80,11 +68,108 @@ func convertCharmFindResult(resp transport.FindResponse) params.FindResponse { return result } +func convertBundle(charms []transport.Charm) *Bundle { + bundle := &Bundle{ + Charms: make([]BundleCharm, len(charms)), + } + for i, v := range charms { + bundle.Charms[i] = BundleCharm{ + Name: v.Name, + } + } + return bundle +} + +func convertCharm(info transport.InfoResponse) (*Charm, []string, error) { + charmHubCharm := Charm{ + UsedBy: info.Entity.UsedBy, + } + var series []string + var err error + if meta := unmarshalCharmMetadata(info.DefaultRelease.Revision.MetadataYAML); meta != nil { + charmHubCharm.Subordinate = meta.Subordinate + charmHubCharm.Relations = transformRelations(meta.Requires, meta.Provides) + + // TODO: hml 2021-04-15 + // Implementation of Manifest in charmhub InfoResponse is + // required. In the default-release channel map. + cm := charmMeta{meta: meta} + series, err = corecharm.ComputedSeries(cm) + if err != nil { + return nil, nil, errors.Trace(err) + } + } + if cfg := unmarshalCharmConfig(info.DefaultRelease.Revision.ConfigYAML); cfg != nil { + charmHubCharm.Config = &charm.Config{ + Options: toCharmOptionMap(cfg), + } + } + return &charmHubCharm, series, nil +} + +func isKubernetes(series []string) bool { + seriesSet := set.NewStrings(series...) + return seriesSet.Contains("kubernetes") +} + +func includeChannel(p []corecharm.Platform, architecture, series string) bool { + allArch := architecture == ArchAll + allSeries := series == SeriesAll + + // If we're searching for everything then we can skip the filtering logic + // and return immediately. + if allArch && allSeries { + return true + } + + archSet := channelArches(p) + seriesSet := channelSeries(p) + + if (allArch || archSet.Contains(architecture)) && + (allSeries || seriesSet.Contains(series) || seriesSet.Contains(SeriesAll)) { + return true + } + return false +} + +func channelSeries(platforms []corecharm.Platform) set.Strings { + series := set.NewStrings() + for _, v := range platforms { + series.Add(v.Series) + } + return series +} + +func channelArches(platforms []corecharm.Platform) set.Strings { + arches := set.NewStrings() + for _, v := range platforms { + arches.Add(v.Architecture) + } + // If the platform contains all the arches, just return them exploded. + // This makes the filtering logic simpler for plucking an architecture out + // of the channels, we should aim to do the same for series. + if arches.Contains(ArchAll) { + return set.NewStrings(arch.AllArches().StringList()...) + } + return arches +} + func publisher(ch transport.Entity) string { publisher, _ := ch.Publisher["display-name"] return publisher } +func categories(cats []transport.Category) []string { + if len(cats) == 0 { + return nil + } + result := make([]string, len(cats)) + for i, v := range cats { + result[i] = v.Name + } + return result +} + // supported defines a tuple of extracted items from a platform. type supported struct { Architectures []string @@ -118,86 +203,23 @@ func transformFindArchitectureSeries(channel transport.FindChannelMap) supported } } -// transformInfoChannelMap returns channel map data in a format that facilitates -// determining track order and open vs closed channels for displaying channel -// data. -func transformInfoChannelMap(channelMap []transport.InfoChannelMap, isKub bool) ([]string, map[string]params.Channel) { - var trackList []string - - seen := set.NewStrings("") - channels := make(map[string]params.Channel, len(channelMap)) - - for _, cm := range channelMap { - ch := cm.Channel - // Per the charmhub/snap channel spec. - if ch.Track == "" { - ch.Track = "latest" - } - chName := ch.Track + "/" + ch.Risk - channels[chName] = params.Channel{ - Revision: cm.Revision.Revision, - ReleasedAt: ch.ReleasedAt, - Risk: ch.Risk, - Track: ch.Track, - Size: cm.Revision.Download.Size, - Version: cm.Revision.Version, - Platforms: convertBasesToPlatforms(cm.Revision.Bases, isKub), - } - if !seen.Contains(ch.Track) { - seen.Add(ch.Track) - trackList = append(trackList, ch.Track) - } +func toCharmOptionMap(config *charm.Config) map[string]charm.Option { + if config == nil { + return nil } - return trackList, channels -} - -// convertBasesToPlatforms converts a base to a platform. Is mean to be a short -// term solution due to schedule. It should be removed once platforms are -// replaced with bases through out the code. -func convertBasesToPlatforms(in []transport.Base, isKub bool) []params.Platform { - out := make([]params.Platform, len(in)) - for i, v := range in { - var series string - if isKub { - series = "kubernetes" - } else { - series, _ = coreseries.VersionSeries(v.Channel) - } - os, _ := coreseries.GetOSFromSeries(series) - out[i] = params.Platform{ - Architecture: v.Architecture, - OS: strings.ToLower(os.String()), - Series: series, - } + result := make(map[string]charm.Option) + for key, value := range config.Options { + result[key] = toParamsCharmOption(value) } - return out + return result } -func convertCharm(info transport.InfoResponse) (*params.CharmHubCharm, []string, bool, error) { - charmHubCharm := params.CharmHubCharm{ - UsedBy: info.Entity.UsedBy, - } - var series []string - var err error - var isKub bool - if meta := unmarshalCharmMetadata(info.DefaultRelease.Revision.MetadataYAML); meta != nil { - charmHubCharm.Subordinate = meta.Subordinate - charmHubCharm.Relations = transformRelations(meta.Requires, meta.Provides) - - // TODO: hml 2021-04-15 - // Implementation of Manifest in charmhub InfoResponse is - // required. In the default-release channel map. - cm := charmMeta{meta: meta} - series, err = corecharm.ComputedSeries(cm) - if err != nil { - return nil, nil, false, errors.Trace(err) - } - isKub = isKubernetes(series) +func toParamsCharmOption(opt charm.Option) charm.Option { + return charm.Option{ + Type: opt.Type, + Description: opt.Description, + Default: opt.Default, } - if cfg := unmarshalCharmConfig(info.DefaultRelease.Revision.ConfigYAML); cfg != nil { - charmHubCharm.Config = params.ToCharmOptionMap(cfg) - } - return &charmHubCharm, series, isKub, nil } func unmarshalCharmMetadata(metadataYAML string) *charm.Meta { @@ -262,19 +284,6 @@ func formatRelationPart(r map[string]charm.Relation) (map[string]string, bool) { return relations, true } -func convertBundle(charms []transport.Charm) *params.CharmHubBundle { - bundle := ¶ms.CharmHubBundle{ - Charms: make([]params.BundleCharm, len(charms)), - } - for i, v := range charms { - bundle.Charms[i] = params.BundleCharm{ - Name: v.Name, - PackageID: v.PackageID, - } - } - return bundle -} - type charmMeta struct { meta *charm.Meta manifest *charm.Manifest @@ -288,9 +297,63 @@ func (c charmMeta) Manifest() *charm.Manifest { return c.manifest } -// Update location of series for kubernetes series once manifest.yaml -// is available. -func isKubernetes(series []string) bool { - seriesSet := set.NewStrings(series...) - return seriesSet.Contains("kubernetes") +// filterChannels returns channel map data in a format that facilitates +// determining track order and open vs closed channels for displaying channel +// data. The result is filtered on series and arch. +func filterChannels(channelMap []transport.InfoChannelMap, isKub bool, arch, series string) ([]string, map[string]Channel) { + var trackList []string + + seen := set.NewStrings("") + channels := make(map[string]Channel, len(channelMap)) + + for _, cm := range channelMap { + ch := cm.Channel + // Per the charmhub/snap channel spec. + if ch.Track == "" { + ch.Track = "latest" + } + if !seen.Contains(ch.Track) { + seen.Add(ch.Track) + trackList = append(trackList, ch.Track) + } + + platforms := convertBasesToPlatforms(cm.Revision.Bases, isKub) + if !includeChannel(platforms, arch, series) { + continue + } + + channel := Channel{ + Revision: cm.Revision.Revision, + ReleasedAt: ch.ReleasedAt, + Risk: ch.Risk, + Track: ch.Track, + Size: cm.Revision.Download.Size, + Version: cm.Revision.Version, + Arches: channelArches(platforms).SortedValues(), + Series: channelSeries(platforms).SortedValues(), + } + + chName := ch.Track + "/" + ch.Risk + channels[chName] = channel + } + return trackList, channels +} + +func convertBasesToPlatforms(in []transport.Base, isKub bool) []corecharm.Platform { + out := make([]corecharm.Platform, len(in)) + for i, v := range in { + var series string + if isKub { + series = "kubernetes" + } else { + series, _ = coreseries.VersionSeries(v.Channel) + } + os, _ := coreseries.GetOSFromSeries(series) + out[i] = corecharm.Platform{ + Architecture: v.Architecture, + OS: strings.ToLower(os.String()), + Series: series, + } + } + return out } diff --git a/cmd/juju/charmhub/convert_test.go b/cmd/juju/charmhub/convert_test.go new file mode 100644 index 00000000000..9a53eceb680 --- /dev/null +++ b/cmd/juju/charmhub/convert_test.go @@ -0,0 +1,225 @@ +// Copyright 2020 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package charmhub + +import ( + "github.com/juju/testing" + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/juju/charmhub/transport" + "github.com/juju/juju/core/arch" +) + +type filterSuite struct { + testing.IsolationSuite +} + +var _ = gc.Suite(&filterSuite{}) + +func (filterSuite) TestFilterChannels(c *gc.C) { + tests := []struct { + Name string + Arch string + Series string + Input []transport.InfoChannelMap + Expected map[string]Channel + }{{ + Name: "match all", + Arch: "all", + Series: "all", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: arch.AllArches().StringList(), + Series: []string{"bionic"}, + }, + }, + }, { + Name: "match all architectures", + Arch: "all", + Series: "bionic", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: arch.AllArches().StringList(), + Series: []string{"bionic"}, + }, + }, + }, { + Name: "match all series", + Arch: "amd64", + Series: "all", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "amd64", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: []string{"amd64"}, + Series: []string{"bionic"}, + }, + }, + }, { + Name: "match only ppc64 with focal series", + Arch: "ppc64", + Series: "focal", + Input: []transport.InfoChannelMap{{ + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "amd64", + }}, + }, + }}, + Expected: map[string]Channel{}, + }, { + Name: "channel has all architectures with same series", + Arch: "amd64", + Series: "bionic", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: arch.AllArches().StringList(), + Series: []string{"bionic"}, + }, + }, + }, { + Name: "channel has all architectures with no matching series", + Arch: "amd64", + Series: "focal", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{}, + }, { + Name: "multiple channels have all architectures with same series", + Arch: "amd64", + Series: "focal", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "amd64", + }}, + }, + }, { + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "20.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: arch.AllArches().StringList(), + Series: []string{"focal"}, + }, + }, + }, { + Name: "multiple channels have all architectures with no matching series", + Arch: "amd64", + Series: "bionic", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "amd64", + }}, + }, + }, { + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "20.04", + Architecture: "all", + }}, + }, + }}, + Expected: map[string]Channel{ + "latest/stable": { + Track: "latest", + Risk: "stable", + Arches: []string{"amd64"}, + Series: []string{"bionic"}, + }, + }, + }, { + Name: "exact match finds no valid channels", + Arch: "ppc64", + Series: "focal", + Input: []transport.InfoChannelMap{{ + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "arm64", + }}, + }, + }, { + Channel: transport.Channel{Risk: "stable"}, + Revision: transport.InfoRevision{ + Bases: []transport.Base{{ + Channel: "18.04", + Architecture: "ppc64", + }}, + }, + }}, + Expected: map[string]Channel{}, + }} + for k, v := range tests { + c.Logf("Test %d %s", k, v.Name) + _, got := filterChannels(v.Input, false, v.Arch, v.Series) + c.Assert(got, jc.DeepEquals, v.Expected) + } +} diff --git a/cmd/juju/charmhub/data.go b/cmd/juju/charmhub/data.go index 58e841c9899..694aecddc4d 100644 --- a/cmd/juju/charmhub/data.go +++ b/cmd/juju/charmhub/data.go @@ -5,11 +5,6 @@ package charmhub import ( "github.com/juju/charm/v9" - "github.com/juju/collections/set" - "github.com/juju/errors" - - "github.com/juju/juju/api/charmhub" - "github.com/juju/juju/core/arch" ) const ( @@ -19,183 +14,6 @@ const ( ArchAll = "all" ) -func convertCharmInfoResult(info charmhub.InfoResponse, arch, series string) (InfoResponse, error) { - channels := filterChannels(info.Channels, arch, series) - - ir := InfoResponse{ - Type: info.Type, - ID: info.ID, - Name: info.Name, - Description: info.Description, - Publisher: info.Publisher, - Summary: info.Summary, - Series: info.Series, - StoreURL: info.StoreURL, - Tags: info.Tags, - Channels: convertChannels(channels), - Tracks: info.Tracks, - } - - var err error - switch ir.Type { - case "bundle": - ir.Bundle, err = convertBundle(info.Bundle) - case "charm": - ir.Charm, err = convertCharm(info.Charm) - } - return ir, errors.Trace(err) -} - -func convertCharmFindResults(responses []charmhub.FindResponse) []FindResponse { - results := make([]FindResponse, len(responses)) - for i, resp := range responses { - results[i] = convertCharmFindResult(resp) - } - return results -} - -func convertCharmFindResult(resp charmhub.FindResponse) FindResponse { - return FindResponse{ - Type: resp.Type, - ID: resp.ID, - Name: resp.Name, - Publisher: resp.Publisher, - Summary: resp.Summary, - Version: resp.Version, - Arches: resp.Arches, - OS: resp.OS, - Series: resp.Series, - StoreURL: resp.StoreURL, - } -} - -func convertBundle(in interface{}) (*Bundle, error) { - inB, ok := in.(*charmhub.Bundle) - if !ok { - return nil, errors.Errorf("unexpected: value is not a bundle") - } - if inB == nil { - return nil, errors.Errorf("bundle is nil") - } - out := Bundle{ - Charms: make([]BundleCharm, len(inB.Charms)), - } - for i, c := range inB.Charms { - out.Charms[i] = BundleCharm{Name: c.Name} - } - return &out, nil -} - -func convertCharm(in interface{}) (*Charm, error) { - inC, ok := in.(*charmhub.Charm) - if !ok { - return nil, errors.Errorf("unexpected: value is not a charm") - } - if inC == nil { - return nil, errors.Errorf("CharmHubCharm is nil") - } - return &Charm{ - Config: inC.Config, - Relations: inC.Relations, - Subordinate: inC.Subordinate, - UsedBy: inC.UsedBy, - }, nil -} - -func convertChannels(in map[string]charmhub.Channel) map[string]Channel { - out := make(map[string]Channel, len(in)) - for k, v := range in { - out[k] = Channel{ - ReleasedAt: v.ReleasedAt, - Track: v.Track, - Risk: v.Risk, - Revision: v.Revision, - Size: v.Size, - Version: v.Version, - Series: channelSeries(v.Platforms).SortedValues(), - Arches: channelArches(v.Platforms).SortedValues(), - } - } - - return out -} - -func filterChannels(in map[string]charmhub.Channel, architecture, series string) map[string]charmhub.Channel { - allArch := architecture == ArchAll - allSeries := series == SeriesAll - - // If we're searching for everything then we can skip the filtering logic - // and return immediately. - if allArch && allSeries { - return in - } - - // Channels that match any part of the criteria should be witnessed and - // kept. - witnessed := make(map[string]charmhub.Channel) - - for k, v := range in { - archSet := channelArches(v.Platforms) - seriesSet := channelSeries(v.Platforms) - - if (allArch || archSet.Contains(architecture)) && - (allSeries || seriesSet.Contains(series) || seriesSet.Contains(SeriesAll)) { - witnessed[k] = v - } - } - return witnessed -} - -func channelSeries(platforms []charmhub.Platform) set.Strings { - series := set.NewStrings() - for _, v := range platforms { - series.Add(v.Series) - } - return series -} - -func channelArches(platforms []charmhub.Platform) set.Strings { - arches := set.NewStrings() - for _, v := range platforms { - arches.Add(v.Architecture) - } - // If the platform contains all the arches, just return them exploded. - // This makes the filtering logic simpler for plucking an architecture out - // of the channels, we should aim to do the same for series. - if arches.Contains(ArchAll) { - return set.NewStrings(arch.AllArches().StringList()...) - } - return arches -} - -func filterFindResults(in []charmhub.FindResponse, architecture, series string) []charmhub.FindResponse { - allArch := architecture == ArchAll - allSeries := series == SeriesAll - - // If we're searching for everything then we can skip the filtering logic - // and return immediately. - if allArch && allSeries { - return in - } - - witnessed := make([]charmhub.FindResponse, 0) - - for _, resp := range in { - archSet := set.NewStrings(resp.Arches...) - if archSet.Contains(ArchAll) { - archSet = set.NewStrings(arch.AllArches().StringList()...) - } - seriesSet := set.NewStrings(resp.Series...) - - if (allArch || archSet.Contains(architecture)) && - (allSeries || seriesSet.Contains(series) || seriesSet.Contains(SeriesAll)) { - witnessed = append(witnessed, resp) - } - } - - return witnessed -} - type InfoResponse struct { Type string `json:"type" yaml:"type"` ID string `json:"id" yaml:"id"` diff --git a/cmd/juju/charmhub/data_test.go b/cmd/juju/charmhub/data_test.go deleted file mode 100644 index e99954ae6e7..00000000000 --- a/cmd/juju/charmhub/data_test.go +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2020 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package charmhub - -import ( - "github.com/juju/testing" - jc "github.com/juju/testing/checkers" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/api/charmhub" -) - -type filterSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&filterSuite{}) - -func (filterSuite) TestFilterChannels(c *gc.C) { - tests := []struct { - Name string - Arch string - Series string - Input map[string]charmhub.Channel - Expected map[string]charmhub.Channel - }{{ - Name: "match all", - Arch: "all", - Series: "all", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - }, { - Name: "match all architectures", - Arch: "all", - Series: "bionic", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - }, { - Name: "match all series", - Arch: "amd64", - Series: "all", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - }, { - Name: "match only ppc64 with focal series", - Arch: "ppc64", - Series: "focal", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{}, - }, { - Name: "channel has all architectures with same series", - Arch: "amd64", - Series: "bionic", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - }, { - Name: "channel has all architectures with no matching series", - Arch: "amd64", - Series: "focal", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{}, - }, { - Name: "multiple channels have all architectures with same series", - Arch: "amd64", - Series: "focal", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "focal", - }}, - }, - "latest/edge": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "focal", - }}, - }, - }, - }, { - Name: "multiple channels have all architectures with no matching series", - Arch: "amd64", - Series: "bionic", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "all", - Series: "focal", - }}, - }, - "latest/edge": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{ - "latest/edge": { - Platforms: []charmhub.Platform{{ - Architecture: "amd64", - Series: "bionic", - }}, - }, - }, - }, { - Name: "exact match finds no valid channels", - Arch: "ppc64", - Series: "focal", - Input: map[string]charmhub.Channel{ - "latest/stable": { - Platforms: []charmhub.Platform{{ - Architecture: "arm64", - Series: "bionic", - }, { - Architecture: "ppc64", - Series: "bionic", - }}, - }, - }, - Expected: map[string]charmhub.Channel{}, - }} - for k, v := range tests { - c.Logf("Test %d %s", k, v.Name) - got := filterChannels(v.Input, v.Arch, v.Series) - c.Assert(got, jc.DeepEquals, v.Expected) - } -} - -func (filterSuite) TestFilterFindResults(c *gc.C) { - tests := []struct { - Name string - Arch string - Series string - Input []charmhub.FindResponse - Expected []charmhub.FindResponse - }{{ - Name: "match all", - Arch: "all", - Series: "all", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"all"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"all"}, - }}, - }, { - Name: "match all architectures", - Arch: "all", - Series: "bionic", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"bionic"}, - }}, - }, { - Name: "match all series", - Arch: "amd64", - Series: "all", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - }, { - Name: "match only ppc64 with focal series", - Arch: "ppc64", - Series: "focal", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{}, - }, { - Name: "response has all architectures with same series", - Arch: "amd64", - Series: "bionic", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"bionic"}, - }}, - }, { - Name: "response has all architectures with no matching series", - Arch: "amd64", - Series: "focal", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{}, - }, { - Name: "multiple responses has all architectures with same series", - Arch: "amd64", - Series: "focal", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"focal"}, - }, { - Name: "black tongue", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"focal"}, - }}, - }, { - Name: "multiple responses has all architectures with no matching series", - Arch: "amd64", - Series: "bionic", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"all"}, - Series: []string{"focal"}, - }, { - Name: "black tongue", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{{ - Name: "black tongue", - Arches: []string{"amd64"}, - Series: []string{"bionic"}, - }}, - }, { - Name: "no valid responses", - Arch: "ppc64", - Series: "focal", - Input: []charmhub.FindResponse{{ - Name: "meshuggah", - Arches: []string{"arm64"}, - Series: []string{"bionic"}, - }, { - Name: "black tongue", - Arches: []string{"ppc64"}, - Series: []string{"bionic"}, - }}, - Expected: []charmhub.FindResponse{}, - }} - for k, v := range tests { - c.Logf("Test %d %s", k, v.Name) - got := filterFindResults(v.Input, v.Arch, v.Series) - c.Assert(got, jc.DeepEquals, v.Expected) - } -} diff --git a/cmd/juju/charmhub/download.go b/cmd/juju/charmhub/download.go index 93686b6c377..391147177f4 100644 --- a/cmd/juju/charmhub/download.go +++ b/cmd/juju/charmhub/download.go @@ -24,12 +24,10 @@ import ( "github.com/juju/juju/charmhub" "github.com/juju/juju/charmhub/transport" jujucmd "github.com/juju/juju/cmd" - "github.com/juju/juju/cmd/modelcmd" "github.com/juju/juju/cmd/output/progress" "github.com/juju/juju/core/arch" corecharm "github.com/juju/juju/core/charm" coreseries "github.com/juju/juju/core/series" - "github.com/juju/juju/environs/config" "github.com/juju/juju/version" ) @@ -54,13 +52,10 @@ See also: // NewDownloadCommand wraps downloadCommand with sane model settings. func NewDownloadCommand() cmd.Command { - return modelcmd.Wrap(&downloadCommand{ + return &downloadCommand{ charmHubCommand: newCharmHubCommand(), orderedSeries: series.SupportedJujuControllerSeries(), - CharmHubClientFunc: func(config charmhub.Config, fs charmhub.FileSystem) (DownloadCommandAPI, error) { - return charmhub.NewClientWithFileSystem(config, fs) - }, - }, modelcmd.WrapSkipModelInit) + } } // downloadCommand supplies the "download" CLI command used for downloading @@ -68,12 +63,9 @@ func NewDownloadCommand() cmd.Command { type downloadCommand struct { *charmHubCommand - CharmHubClientFunc func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) - out cmd.Output channel string - charmHubURL string charmOrBundle string archivePath string pipeToStdout bool @@ -98,8 +90,9 @@ func (c *downloadCommand) Info() *cmd.Info { func (c *downloadCommand) SetFlags(f *gnuflag.FlagSet) { c.charmHubCommand.SetFlags(f) + f.StringVar(&c.arch, "arch", ArchAll, fmt.Sprintf("specify an arch <%s>", c.archArgumentList())) + f.StringVar(&c.series, "series", SeriesAll, "specify a series") f.StringVar(&c.channel, "channel", "", "specify a channel to use instead of the default release") - f.StringVar(&c.charmHubURL, "charmhub-url", "", "override the model config by specifying the Charmhub URL for querying the store") f.StringVar(&c.archivePath, "filepath", "", "filepath location of the charm to download to") } @@ -125,13 +118,6 @@ func (c *downloadCommand) Init(args []string) error { } c.charmOrBundle = args[0] - if c.charmHubURL != "" { - _, err := url.ParseRequestURI(c.charmHubURL) - if err != nil { - return errors.Annotatef(err, "unexpected charmhub-url") - } - } - return nil } @@ -149,42 +135,7 @@ func (c *downloadCommand) validateCharmOrBundle(charmOrBundle string) error { // Run is the business logic of the download command. It implements the meaty // part of the cmd.Command interface. func (c *downloadCommand) Run(cmdContext *cmd.Context) error { - var ( - err error - charmHubURL string - ) - if c.charmHubURL != "" { - charmHubURL = c.charmHubURL - } else { - // This is a horrible workaround for the fact that this command can work - // with and without a bootstrapped controller. - // To correctly handle the fact that we want to lazily connect to a - // controller, we have to grab the model identifier once we know what - // we want to do (based on the flags) and then call the init the model - // callstack. - // The reason this exists is because everything is curated for you, but - // when we do need to customize this workflow, it unfortunately gets in - // the way. - modelIdentifier, _ := c.ModelCommandBase.ModelIdentifier() - if err := c.ModelCommandBase.SetModelIdentifier(modelIdentifier, true); err != nil { - return errors.Trace(err) - } - - if err := c.charmHubCommand.Run(cmdContext); err != nil { - return errors.Trace(err) - } - - charmHubURL, err = c.getCharmHubURL() - if err != nil { - if errors.IsNotImplemented(err) { - cmdContext.Warningf("juju download not supported with controllers < 2.9") - return nil - } - return errors.Trace(err) - } - } - - config, err := charmhub.CharmHubConfigFromURL(charmHubURL, downloadLogger{ + config, err := charmhub.CharmHubConfigFromURL(c.charmHubURL, downloadLogger{ Context: cmdContext, }) if err != nil { @@ -368,40 +319,6 @@ func (c *downloadCommand) calculateHash(path string) (string, error) { return fmt.Sprintf("%x", hash.Sum(nil)), nil } -func (c *downloadCommand) getCharmHubURL() (string, error) { - apiRoot, err := c.APIRootFunc() - if err != nil { - return "", errors.Trace(err) - } - defer func() { _ = apiRoot.Close() }() - - modelConfigClient := c.ModelConfigClientFunc(apiRoot) - defer func() { _ = modelConfigClient.Close() }() - - attrs, err := modelConfigClient.ModelGet() - if err != nil { - return "", errors.Trace(err) - } - - config, err := config.New(config.NoDefaults, attrs) - if err != nil { - return "", errors.Trace(err) - } - - charmHubURL, _ := config.CharmHubURL() - return charmHubURL, nil -} - -// CharmHubClient defines a charmhub client, used for querying the charmhub -// store. -type CharmHubClient interface { - // Refresh returns the charm/bundle response for a given configuration. - Refresh(context.Context, charmhub.RefreshConfig) ([]transport.RefreshResponse, error) - - // Download defines a client for downloading charms directly. - Download(context.Context, *url.URL, string) error -} - type downloadLogger struct { Context *cmd.Context } @@ -420,8 +337,8 @@ func (d downloadLogger) Debugf(msg string, args ...interface{}) { func (d downloadLogger) Tracef(msg string, args ...interface{}) {} -func (d downloadLogger) ChildWithLabels(string, ...string) loggo.Logger { - return loggo.Logger{} +func (d downloadLogger) ChildWithLabels(name string, labels ...string) loggo.Logger { + return logger.ChildWithLabels(name, labels...) } type stdoutFileSystem struct{} diff --git a/cmd/juju/charmhub/download_test.go b/cmd/juju/charmhub/download_test.go index a7ad0dc9790..dc04e360aa3 100644 --- a/cmd/juju/charmhub/download_test.go +++ b/cmd/juju/charmhub/download_test.go @@ -13,12 +13,9 @@ import ( jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" - "github.com/juju/juju/api/base" - basemocks "github.com/juju/juju/api/base/mocks" "github.com/juju/juju/charmhub" "github.com/juju/juju/charmhub/transport" "github.com/juju/juju/cmd/juju/charmhub/mocks" - "github.com/juju/juju/cmd/modelcmd" "github.com/juju/juju/core/arch" "github.com/juju/juju/jujuclient" "github.com/juju/juju/jujuclient/jujuclienttesting" @@ -29,11 +26,9 @@ type downloadSuite struct { testing.FakeJujuXDGDataHomeSuite store *jujuclient.MemStore - downloadCommandAPI *mocks.MockDownloadCommandAPI - modelConfigAPI *mocks.MockModelConfigClient - apiRoot *basemocks.MockAPICallCloser - file *mocks.MockReadSeekCloser - filesystem *mocks.MockFilesystem + charmHubAPI *mocks.MockCharmHubClient + file *mocks.MockReadSeekCloser + filesystem *mocks.MockFilesystem } var _ = gc.Suite(&downloadSuite{}) @@ -64,25 +59,19 @@ func (s *downloadSuite) TestRun(c *gc.C) { url := "http://example.org/" - s.expectModelGet(url) s.expectRefresh(url) s.expectDownload(c, url) s.expectFilesystem(c) command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } - command.SetClientStore(s.store) command.SetFilesystem(s.filesystem) - cmd := modelcmd.Wrap(command, modelcmd.WrapSkipModelInit) - err := cmdtesting.InitCommand(cmd, []string{"test"}) + err := cmdtesting.InitCommand(command, []string{"test"}) c.Assert(err, jc.ErrorIsNil) ctx := commandContextForTest(c) - err = cmd.Run(ctx) + err = command.Run(ctx) c.Assert(err, jc.ErrorIsNil) } @@ -91,23 +80,17 @@ func (s *downloadSuite) TestRunWithStdout(c *gc.C) { url := "http://example.org/" - s.expectModelGet(url) s.expectRefresh(url) s.expectDownload(c, url) command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } - command.SetClientStore(s.store) - cmd := modelcmd.Wrap(command, modelcmd.WrapSkipModelInit) - err := cmdtesting.InitCommand(cmd, []string{"test", "-"}) + err := cmdtesting.InitCommand(command, []string{"test", "-"}) c.Assert(err, jc.ErrorIsNil) ctx := commandContextForTest(c) - err = cmd.Run(ctx) + err = command.Run(ctx) c.Assert(err, jc.ErrorIsNil) } @@ -122,9 +105,6 @@ func (s *downloadSuite) TestRunWithCustomCharmHubURL(c *gc.C) { command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } command.SetFilesystem(s.filesystem) err := cmdtesting.InitCommand(command, []string{"--charmhub-url=" + url, "test"}) @@ -138,25 +118,17 @@ func (s *downloadSuite) TestRunWithCustomCharmHubURL(c *gc.C) { func (s *downloadSuite) TestRunWithUnsupportedSeries(c *gc.C) { defer s.setUpMocks(c).Finish() - url := "http://example.org/" - - s.expectModelGet(url) s.expectRefreshUnsupportedSeries() command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } - command.SetClientStore(s.store) command.SetFilesystem(s.filesystem) - cmd := modelcmd.Wrap(command, modelcmd.WrapSkipModelInit) - err := cmdtesting.InitCommand(cmd, []string{"test"}) + err := cmdtesting.InitCommand(command, []string{"test"}) c.Assert(err, jc.ErrorIsNil) ctx := commandContextForTest(c) - err = cmd.Run(ctx) + err = command.Run(ctx) c.Assert(err, gc.ErrorMatches, "test does not support series focal in channel stable. Supported series are bionic, trusty, xenial.") } @@ -167,12 +139,9 @@ func (s *downloadSuite) TestRunWithCustomInvalidCharmHubURL(c *gc.C) { command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } err := cmdtesting.InitCommand(command, []string{"--charmhub-url=" + url, "test"}) - c.Assert(err, gc.ErrorMatches, `unexpected charmhub-url: parse "meshuggah": invalid URI for request`) + c.Assert(err, gc.ErrorMatches, `invalid charmhub-url: parse "meshuggah": invalid URI for request`) } func (s *downloadSuite) TestRunWithInvalidStdout(c *gc.C) { @@ -180,9 +149,6 @@ func (s *downloadSuite) TestRunWithInvalidStdout(c *gc.C) { command := &downloadCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (DownloadCommandAPI, error) { - return s.downloadCommandAPI, nil - }, } err := cmdtesting.InitCommand(command, []string{"test", "_"}) c.Assert(err, gc.ErrorMatches, `expected a charm or bundle name, followed by hyphen to pipe to stdout`) @@ -191,11 +157,8 @@ func (s *downloadSuite) TestRunWithInvalidStdout(c *gc.C) { func (s *downloadSuite) newCharmHubCommand() *charmHubCommand { return &charmHubCommand{ arches: arch.AllArches(), - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - ModelConfigClientFunc: func(api base.APICallCloser) ModelConfigClient { - return s.modelConfigAPI + CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (CharmHubClient, error) { + return s.charmHubAPI, nil }, } } @@ -203,13 +166,7 @@ func (s *downloadSuite) newCharmHubCommand() *charmHubCommand { func (s *downloadSuite) setUpMocks(c *gc.C) *gomock.Controller { ctrl := gomock.NewController(c) - s.downloadCommandAPI = mocks.NewMockDownloadCommandAPI(ctrl) - - s.modelConfigAPI = mocks.NewMockModelConfigClient(ctrl) - s.modelConfigAPI.EXPECT().Close().AnyTimes() - - s.apiRoot = basemocks.NewMockAPICallCloser(ctrl) - s.apiRoot.EXPECT().Close().AnyTimes() + s.charmHubAPI = mocks.NewMockCharmHubClient(ctrl) s.file = mocks.NewMockReadSeekCloser(ctrl) s.filesystem = mocks.NewMockFilesystem(ctrl) @@ -217,17 +174,8 @@ func (s *downloadSuite) setUpMocks(c *gc.C) *gomock.Controller { return ctrl } -func (s *downloadSuite) expectModelGet(charmHubURL string) { - s.modelConfigAPI.EXPECT().ModelGet().Return(map[string]interface{}{ - "type": "my-type", - "name": "my-name", - "uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d", - "charmhub-url": charmHubURL, - }, nil) -} - func (s *downloadSuite) expectRefresh(charmHubURL string) { - s.downloadCommandAPI.EXPECT().Refresh(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, cfg charmhub.RefreshConfig) ([]transport.RefreshResponse, error) { + s.charmHubAPI.EXPECT().Refresh(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, cfg charmhub.RefreshConfig) ([]transport.RefreshResponse, error) { instanceKey := charmhub.ExtractConfigInstanceKey(cfg) return []transport.RefreshResponse{{ @@ -245,7 +193,7 @@ func (s *downloadSuite) expectRefresh(charmHubURL string) { } func (s *downloadSuite) expectRefreshUnsupportedSeries() { - s.downloadCommandAPI.EXPECT().Refresh(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, cfg charmhub.RefreshConfig) ([]transport.RefreshResponse, error) { + s.charmHubAPI.EXPECT().Refresh(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, cfg charmhub.RefreshConfig) ([]transport.RefreshResponse, error) { instanceKey := charmhub.ExtractConfigInstanceKey(cfg) return []transport.RefreshResponse{{ @@ -293,7 +241,7 @@ func (s *downloadSuite) expectRefreshUnsupportedSeries() { func (s *downloadSuite) expectDownload(c *gc.C, charmHubURL string) { resourceURL, err := url.Parse(charmHubURL) c.Assert(err, jc.ErrorIsNil) - s.downloadCommandAPI.EXPECT().Download(gomock.Any(), resourceURL, "test_e3b0c44.charm", gomock.Any()).Return(nil) + s.charmHubAPI.EXPECT().Download(gomock.Any(), resourceURL, "test_e3b0c44.charm", gomock.Any()).Return(nil) } func (s *downloadSuite) expectFilesystem(c *gc.C) { diff --git a/cmd/juju/charmhub/find.go b/cmd/juju/charmhub/find.go index 791e53e71bc..bc5e7bd6ffd 100644 --- a/cmd/juju/charmhub/find.go +++ b/cmd/juju/charmhub/find.go @@ -4,6 +4,7 @@ package charmhub import ( + "context" "fmt" "io" @@ -11,11 +12,9 @@ import ( "github.com/juju/errors" "github.com/juju/gnuflag" - "github.com/juju/juju/api/base" - "github.com/juju/juju/api/charmhub" - "github.com/juju/juju/apiserver/params" + "github.com/juju/juju/charmhub" + "github.com/juju/juju/charmhub/transport" jujucmd "github.com/juju/juju/cmd" - "github.com/juju/juju/cmd/modelcmd" ) const ( @@ -34,23 +33,14 @@ See also: // NewFindCommand wraps findCommand with sane model settings. func NewFindCommand() cmd.Command { - cmd := &findCommand{ - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return charmhub.NewClient(api) - }, + return &findCommand{ + charmHubCommand: newCharmHubCommand(), } - cmd.APIRootFunc = func() (base.APICallCloser, error) { - return cmd.NewAPIRoot() - } - return modelcmd.Wrap(cmd) } // findCommand supplies the "find" CLI command used to display find information. type findCommand struct { - modelcmd.ModelCommandBase - - APIRootFunc func() (base.APICallCloser, error) - CharmHubClientFunc func(base.APICallCloser) FindCommandAPI + *charmHubCommand out cmd.Output warningLog Log @@ -64,7 +54,7 @@ type findCommand struct { columns string } -// Find returns help related info about the command, it implements +// Info returns help related info about the command, it implements // part of the cmd.Command interface. func (c *findCommand) Info() *cmd.Info { info := &cmd.Info{ @@ -79,7 +69,7 @@ func (c *findCommand) Info() *cmd.Info { // SetFlags defines flags which can be used with the find command. // It implements part of the cmd.Command interface. func (c *findCommand) SetFlags(f *gnuflag.FlagSet) { - c.ModelCommandBase.SetFlags(f) + c.charmHubCommand.SetFlags(f) c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ "yaml": cmd.FormatYaml, @@ -111,6 +101,10 @@ func (c *findCommand) SetFlags(f *gnuflag.FlagSet) { // Init initializes the find command, including validating the provided // flags. It implements part of the cmd.Command interface. func (c *findCommand) Init(args []string) error { + if err := c.charmHubCommand.Init(args); err != nil { + return errors.Trace(err) + } + // We allow searching of empty queries, which will return a list of // "interesting charms". if len(args) > 0 { @@ -137,18 +131,24 @@ func (c *findCommand) Init(args []string) error { // Run is the business logic of the find command. It implements the meaty // part of the cmd.Command interface. -func (c *findCommand) Run(ctx *cmd.Context) error { - apiRoot, err := c.APIRootFunc() +func (c *findCommand) Run(cmdContext *cmd.Context) error { + config, err := charmhub.CharmHubConfigFromURL(c.charmHubURL, logger) if err != nil { return errors.Trace(err) } - defer func() { _ = apiRoot.Close() }() - charmHubClient := c.CharmHubClientFunc(apiRoot) + client, err := c.CharmHubClientFunc(config, charmhub.DefaultFileSystem()) + if err != nil { + return errors.Trace(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() options := populateFindOptions(c) - results, err := charmHubClient.Find(c.query, options...) - if params.IsCodeNotFound(err) { + + results, err := client.Find(ctx, c.query, options...) + if errors.IsNotFound(err) { return errors.Wrap(err, errors.Errorf("Nothing found for query %q.", c.query)) } else if err != nil { return errors.Trace(err) @@ -158,12 +158,12 @@ func (c *findCommand) Run(ctx *cmd.Context) error { // when we get invalid data from the API. // We store it on the command before attempting to output, so we can pick // it up later. - c.warningLog = ctx.Warningf + c.warningLog = cmdContext.Warningf - return c.output(ctx, results, c.query == "" && len(options) == 0) + return c.output(cmdContext, results, c.query == "" && len(options) == 0) } -func (c *findCommand) output(ctx *cmd.Context, results []charmhub.FindResponse, emptyQuery bool) error { +func (c *findCommand) output(ctx *cmd.Context, results []transport.FindResponse, emptyQuery bool) error { tabular := c.out.Name() == "tabular" if tabular { // If the results are empty, we should return a helpful message to the diff --git a/cmd/juju/charmhub/find_test.go b/cmd/juju/charmhub/find_test.go index b8c5acaeea7..649c5bf9f35 100644 --- a/cmd/juju/charmhub/find_test.go +++ b/cmd/juju/charmhub/find_test.go @@ -10,9 +10,8 @@ import ( jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" - "github.com/juju/juju/api/base" - basemocks "github.com/juju/juju/api/base/mocks" - "github.com/juju/juju/api/charmhub" + "github.com/juju/juju/charmhub" + "github.com/juju/juju/charmhub/transport" "github.com/juju/juju/cmd/juju/charmhub/mocks" "github.com/juju/juju/core/arch" ) @@ -20,8 +19,7 @@ import ( type findSuite struct { testing.IsolationSuite - findCommandAPI *mocks.MockFindCommandAPI - apiRoot *basemocks.MockAPICallCloser + charmHubAPI *mocks.MockCharmHubClient } var _ = gc.Suite(&findSuite{}) @@ -29,7 +27,8 @@ var _ = gc.Suite(&findSuite{}) func (s *findSuite) TestInitNoArgs(c *gc.C) { // You can query the find api with no arguments. command := &findCommand{ - columns: "nbvps", + charmHubCommand: s.newCharmHubCommand(), + columns: "nbvps", } err := command.Init([]string{}) c.Assert(err, jc.ErrorIsNil) @@ -37,7 +36,8 @@ func (s *findSuite) TestInitNoArgs(c *gc.C) { func (s *findSuite) TestInitSuccess(c *gc.C) { command := &findCommand{ - columns: "nbvps", + charmHubCommand: s.newCharmHubCommand(), + columns: "nbvps", } err := command.Init([]string{"test"}) c.Assert(err, jc.ErrorIsNil) @@ -48,12 +48,7 @@ func (s *findSuite) TestRun(c *gc.C) { s.expectFind() command := &findCommand{ - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return s.findCommandAPI - }, + charmHubCommand: s.newCharmHubCommand(), } err := cmdtesting.InitCommand(command, []string{"test"}) @@ -69,12 +64,7 @@ func (s *findSuite) TestRunJSON(c *gc.C) { s.expectFind() command := &findCommand{ - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return s.findCommandAPI - }, + charmHubCommand: s.newCharmHubCommand(), } err := cmdtesting.InitCommand(command, []string{"test", "--format", "json"}) @@ -83,7 +73,7 @@ func (s *findSuite) TestRunJSON(c *gc.C) { ctx := commandContextForTest(c) err = command.Run(ctx) c.Assert(err, jc.ErrorIsNil) - c.Assert(cmdtesting.Stdout(ctx), gc.Equals, `[{"type":"object","id":"charmCHARMcharmCHARMcharmCHARM01","name":"wordpress","publisher":"Wordress Charmers","summary":"WordPress is a full featured web blogging tool, this charm deploys it.","version":"1.0.3","architectures":["all"],"series":["bionic"],"store-url":"https://someurl.com/wordpress"}] + c.Assert(cmdtesting.Stdout(ctx), gc.Equals, `[{"type":"object","id":"charmCHARMcharmCHARMcharmCHARM01","name":"wordpress","publisher":"Wordress Charmers","summary":"WordPress is a full featured web blogging tool, this charm deploys it.","version":"1.0.3","architectures":["all"],"os":["ubuntu"],"series":["bionic"],"store-url":"https://someurl.com/wordpress"}] `) } @@ -92,12 +82,7 @@ func (s *findSuite) TestRunYAML(c *gc.C) { s.expectFind() command := &findCommand{ - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return s.findCommandAPI - }, + charmHubCommand: s.newCharmHubCommand(), } err := cmdtesting.InitCommand(command, []string{"test", "--format", "yaml"}) @@ -115,6 +100,8 @@ func (s *findSuite) TestRunYAML(c *gc.C) { version: 1.0.3 architectures: - all + os: + - ubuntu series: - bionic store-url: https://someurl.com/wordpress @@ -126,12 +113,7 @@ func (s *findSuite) TestRunWithType(c *gc.C) { s.expectFind() command := &findCommand{ - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return s.findCommandAPI - }, + charmHubCommand: s.newCharmHubCommand(), } err := cmdtesting.InitCommand(command, []string{"test", "--type", "bundle"}) @@ -147,12 +129,7 @@ func (s *findSuite) TestRunWithNoType(c *gc.C) { s.expectFind() command := &findCommand{ - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - CharmHubClientFunc: func(api base.APICallCloser) FindCommandAPI { - return s.findCommandAPI - }, + charmHubCommand: s.newCharmHubCommand(), } err := cmdtesting.InitCommand(command, []string{"test", "--type", ""}) @@ -166,34 +143,37 @@ func (s *findSuite) TestRunWithNoType(c *gc.C) { func (s *findSuite) newCharmHubCommand() *charmHubCommand { return &charmHubCommand{ arches: arch.AllArches(), - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil + CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (CharmHubClient, error) { + return s.charmHubAPI, nil }, } } func (s *findSuite) setUpMocks(c *gc.C) *gomock.Controller { ctrl := gomock.NewController(c) - - s.findCommandAPI = mocks.NewMockFindCommandAPI(ctrl) - s.findCommandAPI.EXPECT().Close().AnyTimes() - - s.apiRoot = basemocks.NewMockAPICallCloser(ctrl) - s.apiRoot.EXPECT().Close().AnyTimes() - + s.charmHubAPI = mocks.NewMockCharmHubClient(ctrl) return ctrl } func (s *findSuite) expectFind() { - s.findCommandAPI.EXPECT().Find("test", gomock.Any()).Return([]charmhub.FindResponse{{ - Name: "wordpress", - Type: "object", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Version: "1.0.3", - Arches: []string{"all"}, - Series: []string{"bionic"}, - StoreURL: "https://someurl.com/wordpress", + s.charmHubAPI.EXPECT().Find(gomock.Any(), "test", gomock.Any()).Return([]transport.FindResponse{{ + Name: "wordpress", + Type: "object", + ID: "charmCHARMcharmCHARMcharmCHARM01", + Entity: transport.Entity{ + Publisher: map[string]string{"display-name": "Wordress Charmers"}, + Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", + StoreURL: "https://someurl.com/wordpress", + }, + DefaultRelease: transport.FindChannelMap{ + Revision: transport.FindRevision{ + Version: "1.0.3", + Bases: []transport.Base{{ + Name: "ubuntu", + Channel: "18.04", + Architecture: "all", + }}, + }, + }, }}, nil) } diff --git a/cmd/juju/charmhub/info.go b/cmd/juju/charmhub/info.go index 10a54624c1e..0e844956ac0 100644 --- a/cmd/juju/charmhub/info.go +++ b/cmd/juju/charmhub/info.go @@ -4,6 +4,8 @@ package charmhub import ( + "context" + "fmt" "io" "github.com/juju/charm/v9" @@ -11,21 +13,17 @@ import ( "github.com/juju/errors" "github.com/juju/gnuflag" - "github.com/juju/juju/api/base" - "github.com/juju/juju/api/charmhub" - "github.com/juju/juju/apiserver/params" + "github.com/juju/juju/charmhub" jujucmd "github.com/juju/juju/cmd" - "github.com/juju/juju/cmd/modelcmd" ) const ( infoSummary = "Displays detailed information about CharmHub charms." infoDoc = ` -The charm can be specified by name or by path. Names are looked for both in the -store and in the deployed charms. +The charm can be specified by name or by path. -Channels displayed are supported by the default series for this model. To see -channels supported with other series, use the --series flag. +Channels displayed are supported by any series. +To see channels supported for only a specific series, use the --series flag. Examples: juju info postgresql @@ -38,12 +36,9 @@ See also: // NewInfoCommand wraps infoCommand with sane model settings. func NewInfoCommand() cmd.Command { - return modelcmd.Wrap(&infoCommand{ + return &infoCommand{ charmHubCommand: newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return charmhub.NewClient(api) - }, - }) + } } // infoCommand supplies the "info" CLI command used to display info @@ -54,8 +49,6 @@ type infoCommand struct { out cmd.Output warningLog Log - CharmHubClientFunc func(base.APICallCloser) InfoCommandAPI - config bool channel string charmOrBundle string @@ -80,6 +73,8 @@ func (c *infoCommand) Info() *cmd.Info { func (c *infoCommand) SetFlags(f *gnuflag.FlagSet) { c.charmHubCommand.SetFlags(f) + f.StringVar(&c.arch, "arch", ArchAll, fmt.Sprintf("specify an arch <%s>", c.archArgumentList())) + f.StringVar(&c.series, "series", SeriesAll, "specify a series") f.StringVar(&c.channel, "channel", "", "specify a channel to use instead of the default release") f.BoolVar(&c.config, "config", false, "display config for this charm") f.StringVar(&c.unicode, "unicode", "auto", "display output using unicode ") @@ -129,46 +124,48 @@ func (c *infoCommand) validateCharmOrBundle(charmOrBundle string) error { // Run is the business logic of the info command. It implements the meaty // part of the cmd.Command interface. -func (c *infoCommand) Run(ctx *cmd.Context) error { - if err := c.charmHubCommand.Run(ctx); err != nil { +func (c *infoCommand) Run(cmdContext *cmd.Context) error { + config, err := charmhub.CharmHubConfigFromURL(c.charmHubURL, logger) + if err != nil { return errors.Trace(err) } - apiRoot, err := c.APIRootFunc() + client, err := c.CharmHubClientFunc(config, charmhub.DefaultFileSystem()) if err != nil { return errors.Trace(err) } - defer func() { _ = apiRoot.Close() }() - charmHubClient := c.CharmHubClientFunc(apiRoot) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - channel := c.channel - if channel != "" { + var options []charmhub.InfoOption + if c.channel != "" { charmChannel, err := charm.ParseChannelNormalize(c.channel) if err != nil { return errors.Trace(err) } - channel = charmChannel.String() + options = append(options, charmhub.WithInfoChannel(charmChannel.String())) } - info, err := charmHubClient.Info(c.charmOrBundle, charmhub.WithInfoChannel(channel)) - if params.IsCodeNotFound(err) { + info, err := client.Info(ctx, c.charmOrBundle, options...) + if errors.IsNotFound(err) { return errors.Wrap(err, errors.Errorf("No information found for charm or bundle with the name %q", c.charmOrBundle)) } else if err != nil { return errors.Trace(err) } + view, err := convertCharmInfoResult(info, c.arch, c.series) + if err != nil { + return errors.Trace(err) + } + // This is a side effect of the formatting code not wanting to error out // when we get invalid data from the API. // We store it on the command before attempting to output, so we can pick // it up later. - c.warningLog = ctx.Warningf + c.warningLog = cmdContext.Warningf - view, err := convertCharmInfoResult(info, c.arch, c.series) - if err != nil { - return errors.Trace(err) - } - return c.out.Write(ctx, &view) + return c.out.Write(cmdContext, &view) } func (c *infoCommand) formatter(writer io.Writer, value interface{}) error { diff --git a/cmd/juju/charmhub/info_test.go b/cmd/juju/charmhub/info_test.go index 3642242932c..0c86973dcf0 100644 --- a/cmd/juju/charmhub/info_test.go +++ b/cmd/juju/charmhub/info_test.go @@ -5,27 +5,21 @@ package charmhub import ( "github.com/golang/mock/gomock" - "github.com/juju/charm/v9" "github.com/juju/cmd/v3/cmdtesting" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" - "github.com/juju/juju/api/base" - basemocks "github.com/juju/juju/api/base/mocks" - "github.com/juju/juju/core/arch" - "github.com/juju/juju/environs/config" - - "github.com/juju/juju/api/charmhub" + "github.com/juju/juju/charmhub" + "github.com/juju/juju/charmhub/transport" "github.com/juju/juju/cmd/juju/charmhub/mocks" + "github.com/juju/juju/core/arch" ) type infoSuite struct { testing.IsolationSuite - infoCommandAPI *mocks.MockInfoCommandAPI - modelConfigAPI *mocks.MockModelConfigClient - apiRoot *basemocks.MockAPICallCloser + charmHubAPI *mocks.MockCharmHubClient } var _ = gc.Suite(&infoSuite{}) @@ -64,9 +58,6 @@ func (s *infoSuite) TestRun(c *gc.C) { command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test"}) @@ -83,9 +74,6 @@ func (s *infoSuite) TestRunJSON(c *gc.C) { command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test", "--format", "json"}) @@ -104,9 +92,6 @@ func (s *infoSuite) TestRunJSONSpecifySeriesNotDefault(c *gc.C) { command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test", "--format", "json", "--series", "xenial"}) @@ -125,9 +110,6 @@ func (s *infoSuite) TestRunJSONSpecifyArch(c *gc.C) { command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test", "--format", "json", "--arch", "amd64"}) @@ -146,9 +128,6 @@ func (s *infoSuite) TestRunJSONWithSeriesFoundChannel(c *gc.C) { command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test", "--series", "focal", "--format", "json"}) @@ -161,59 +140,12 @@ func (s *infoSuite) TestRunJSONWithSeriesFoundChannel(c *gc.C) { `) } -func (s *infoSuite) TestRunJSONDefaultSeriesFoundChannel(c *gc.C) { - defer s.setUpMocks(c).Finish() - s.expectInfo() - s.expectModelConfig(c, "bionic") - - command := &infoCommand{ - charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, - } - - err := cmdtesting.InitCommand(command, []string{"test", "--series", "", "--format", "json"}) - c.Assert(err, jc.ErrorIsNil) - - ctx := commandContextForTest(c) - err = command.Run(ctx) - c.Assert(err, jc.ErrorIsNil) - c.Assert(cmdtesting.Stdout(ctx), gc.Equals, `{"type":"charm","id":"charmCHARMcharmCHARMcharmCHARM01","name":"wordpress","description":"This will install and setup WordPress optimized to run in the cloud.","publisher":"Wordress Charmers","summary":"WordPress is a full featured web blogging tool, this charm deploys it.","series":["bionic","xenial"],"store-url":"https://someurl.com/wordpress","tags":["app","seven"],"charm":{"config":{"Options":{"agility-ratio":{"Type":"float","Description":"A number from 0 to 1 indicating agility.","Default":null},"outlook":{"Type":"string","Description":"No default outlook.","Default":null},"reticulate-splines":{"Type":"boolean","Description":"Whether to reticulate splines on launch, or not.","Default":null},"skill-level":{"Type":"int","Description":"A number indicating skill.","Default":null},"subtitle":{"Type":"string","Description":"An optional subtitle used for the application.","Default":""},"title":{"Type":"string","Description":"A descriptive title used for the application.","Default":"My Title"},"username":{"Type":"string","Description":"The name of the initial account (given admin permissions).","Default":"admin001"}}},"relations":{"provides":{"source":"dummy-token"},"requires":{"sink":"dummy-token"}},"used-by":["wordpress-everlast","wordpress-jorge","wordpress-site"]},"channel-map":{"latest/stable":{"released-at":"2019-12-16T19:44:44.076943+00:00","track":"latest","risk":"stable","revision":16,"size":12042240,"version":"1.0.3","architectures":["amd64"],"series":["bionic","xenial"]}},"tracks":["latest"]} -`) -} - -func (s *infoSuite) TestRunJSONDefaultSeriesNotFoundNoChannel(c *gc.C) { - defer s.setUpMocks(c).Finish() - s.expectInfo() - s.expectModelConfig(c, "focal") - - command := &infoCommand{ - charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, - } - - err := cmdtesting.InitCommand(command, []string{"test", "--series", "", "--format", "json"}) - c.Assert(err, jc.ErrorIsNil) - - ctx := commandContextForTest(c) - err = command.Run(ctx) - c.Assert(err, jc.ErrorIsNil) - c.Assert(cmdtesting.Stdout(ctx), gc.Equals, `{"type":"charm","id":"charmCHARMcharmCHARMcharmCHARM01","name":"wordpress","description":"This will install and setup WordPress optimized to run in the cloud.","publisher":"Wordress Charmers","summary":"WordPress is a full featured web blogging tool, this charm deploys it.","series":["bionic","xenial"],"store-url":"https://someurl.com/wordpress","tags":["app","seven"],"charm":{"config":{"Options":{"agility-ratio":{"Type":"float","Description":"A number from 0 to 1 indicating agility.","Default":null},"outlook":{"Type":"string","Description":"No default outlook.","Default":null},"reticulate-splines":{"Type":"boolean","Description":"Whether to reticulate splines on launch, or not.","Default":null},"skill-level":{"Type":"int","Description":"A number indicating skill.","Default":null},"subtitle":{"Type":"string","Description":"An optional subtitle used for the application.","Default":""},"title":{"Type":"string","Description":"A descriptive title used for the application.","Default":"My Title"},"username":{"Type":"string","Description":"The name of the initial account (given admin permissions).","Default":"admin001"}}},"relations":{"provides":{"source":"dummy-token"},"requires":{"sink":"dummy-token"}},"used-by":["wordpress-everlast","wordpress-jorge","wordpress-site"]},"channel-map":{},"tracks":["latest"]} -`) -} - func (s *infoSuite) TestRunYAML(c *gc.C) { defer s.setUpMocks(c).Finish() s.expectInfo() command := &infoCommand{ charmHubCommand: s.newCharmHubCommand(), - CharmHubClientFunc: func(api base.APICallCloser) InfoCommandAPI { - return s.infoCommandAPI - }, } err := cmdtesting.InitCommand(command, []string{"test", "--format", "yaml"}) @@ -293,87 +225,112 @@ tracks: func (s *infoSuite) newCharmHubCommand() *charmHubCommand { return &charmHubCommand{ arches: arch.AllArches(), - APIRootFunc: func() (base.APICallCloser, error) { - return s.apiRoot, nil - }, - ModelConfigClientFunc: func(api base.APICallCloser) ModelConfigClient { - return s.modelConfigAPI + CharmHubClientFunc: func(charmhub.Config, charmhub.FileSystem) (CharmHubClient, error) { + return s.charmHubAPI, nil }, } } func (s *infoSuite) setUpMocks(c *gc.C) *gomock.Controller { ctrl := gomock.NewController(c) - - s.infoCommandAPI = mocks.NewMockInfoCommandAPI(ctrl) - s.infoCommandAPI.EXPECT().Close().AnyTimes() - - s.modelConfigAPI = mocks.NewMockModelConfigClient(ctrl) - s.modelConfigAPI.EXPECT().Close().AnyTimes() - - s.apiRoot = basemocks.NewMockAPICallCloser(ctrl) - s.apiRoot.EXPECT().Close().AnyTimes() - + s.charmHubAPI = mocks.NewMockCharmHubClient(ctrl) return ctrl } func (s *infoSuite) expectInfo() { - s.infoCommandAPI.EXPECT().Info("test", gomock.Any()).Return(charmhub.InfoResponse{ - Name: "wordpress", - Type: "charm", - ID: "charmCHARMcharmCHARMcharmCHARM01", - Description: "This will install and setup WordPress optimized to run in the cloud.", - Publisher: "Wordress Charmers", - Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", - Tracks: []string{"latest"}, - Series: []string{"bionic", "xenial"}, - StoreURL: "https://someurl.com/wordpress", - Tags: []string{"app", "seven"}, - Channels: map[string]charmhub.Channel{ - "latest/stable": { - ReleasedAt: "2019-12-16T19:44:44.076943+00:00", - Track: "latest", - Risk: "stable", - Size: 12042240, - Revision: 16, - Version: "1.0.3", - Platforms: []charmhub.Platform{ - {Architecture: "amd64", Series: "bionic"}, - {Architecture: "amd64", Series: "xenial"}, - }, + s.charmHubAPI.EXPECT().Info(gomock.Any(), "test", gomock.Any()).Return(transport.InfoResponse{ + Name: "wordpress", + Type: "charm", + ID: "charmCHARMcharmCHARMcharmCHARM01", + Entity: transport.Entity{ + Description: "This will install and setup WordPress optimized to run in the cloud.", + Publisher: map[string]string{"display-name": "Wordress Charmers"}, + Summary: "WordPress is a full featured web blogging tool, this charm deploys it.", + StoreURL: "https://someurl.com/wordpress", + Categories: []transport.Category{{ + Name: "app", + }, { + Name: "seven", }}, - Charm: &charmhub.Charm{ - Subordinate: false, - Config: &charm.Config{ - Options: map[string]charm.Option{ - "reticulate-splines": {Type: "boolean", Description: "Whether to reticulate splines on launch, or not."}, - "title": {Type: "string", Description: "A descriptive title used for the application.", Default: "My Title"}, - "subtitle": {Type: "string", Description: "An optional subtitle used for the application.", Default: ""}, - "outlook": {Type: "string", Description: "No default outlook."}, - "username": {Type: "string", Description: "The name of the initial account (given admin permissions).", Default: "admin001"}, - "skill-level": {Type: "int", Description: "A number indicating skill."}, - "agility-ratio": {Type: "float", Description: "A number from 0 to 1 indicating agility."}, - }, - }, - Relations: map[string]map[string]string{ - "provides": {"source": "dummy-token"}, - "requires": {"sink": "dummy-token"}}, UsedBy: []string{ "wordpress-everlast", "wordpress-jorge", "wordpress-site", }, }, + DefaultRelease: transport.InfoChannelMap{ + Revision: transport.InfoRevision{ + MetadataYAML: entityMeta, + ConfigYAML: entityConfig, + }, + }, + ChannelMap: []transport.InfoChannelMap{{ + Channel: transport.Channel{ + ReleasedAt: "2019-12-16T19:44:44.076943+00:00", + Track: "latest", + Risk: "stable", + }, + Revision: transport.InfoRevision{ + Revision: 16, + Version: "1.0.3", + Download: transport.Download{ + Size: 12042240, + }, + Bases: []transport.Base{{ + Name: "ubuntu", + Channel: "18.04", + Architecture: "amd64", + }, { + Name: "ubuntu", + Channel: "16.04", + Architecture: "amd64", + }}, + }, + }}, }, nil) } -func (s *infoSuite) expectModelConfig(c *gc.C, series string) { - cfg, err := config.New(config.UseDefaults, map[string]interface{}{ - "default-series": series, - "type": "my-type", - "name": "my-name", - "uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d", - }) - c.Assert(err, jc.ErrorIsNil) - s.modelConfigAPI.EXPECT().ModelGet().Return(cfg.AllAttrs(), nil) -} +var entityMeta = ` +name: myname +version: 1.0.3 +subordinate: false +summary: A charm or bundle. +description: | + This will install and setup services optimized to run in the cloud. + By default it will place Ngnix configured to scale horizontally + with Nginx's reverse proxy. +series: [bionic, xenial] +provides: + source: + interface: dummy-token +requires: + sink: + interface: dummy-token +` + +var entityConfig = ` +options: + title: + default: My Title + description: A descriptive title used for the application. + type: string + subtitle: + default: "" + description: An optional subtitle used for the application. + outlook: + description: No default outlook. + # type defaults to string in python + username: + default: admin001 + description: The name of the initial account (given admin permissions). + type: string + skill-level: + description: A number indicating skill. + type: int + agility-ratio: + description: A number from 0 to 1 indicating agility. + type: float + reticulate-splines: + description: Whether to reticulate splines on launch, or not. + type: boolean +` diff --git a/cmd/juju/charmhub/interface.go b/cmd/juju/charmhub/interface.go index 73d7053b79b..79ae46f88ee 100644 --- a/cmd/juju/charmhub/interface.go +++ b/cmd/juju/charmhub/interface.go @@ -7,7 +7,6 @@ import ( "context" "net/url" - apicharmhub "github.com/juju/juju/api/charmhub" "github.com/juju/juju/charmhub" "github.com/juju/juju/charmhub/transport" ) @@ -20,26 +19,11 @@ type Printer interface { // Log describes a log format function to output to. type Log = func(format string, params ...interface{}) -// InfoCommandAPI describes API methods required to execute the info command. -type InfoCommandAPI interface { - Info(string, ...apicharmhub.InfoOption) (apicharmhub.InfoResponse, error) - Close() error -} - -// FindCommandAPI describes API methods required to execute the find command. -type FindCommandAPI interface { - Find(string, ...apicharmhub.FindOption) ([]apicharmhub.FindResponse, error) - Close() error -} - -// DownloadCommandAPI describes API methods required to execute the download -// command. -type DownloadCommandAPI interface { - Info(context.Context, string, ...charmhub.InfoOption) (transport.InfoResponse, error) +// CharmHubClient represents a CharmHub Client for making queries to the CharmHub API. +type CharmHubClient interface { + URL() string + Info(ctx context.Context, name string, options ...charmhub.InfoOption) (transport.InfoResponse, error) + Find(ctx context.Context, query string, options ...charmhub.FindOption) ([]transport.FindResponse, error) Refresh(context.Context, charmhub.RefreshConfig) ([]transport.RefreshResponse, error) - Download(context.Context, *url.URL, string, ...charmhub.DownloadOption) error -} - -type ModelConfigGetter interface { - ModelGet() (map[string]interface{}, error) + Download(ctx context.Context, resourceURL *url.URL, archivePath string, options ...charmhub.DownloadOption) error } diff --git a/cmd/juju/charmhub/mocks/api_mock.go b/cmd/juju/charmhub/mocks/api_mock.go index 7a618e07f07..ee0b29fd141 100644 --- a/cmd/juju/charmhub/mocks/api_mock.go +++ b/cmd/juju/charmhub/mocks/api_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/juju/juju/cmd/juju/charmhub (interfaces: DownloadCommandAPI,InfoCommandAPI,FindCommandAPI,ModelConfigClient,ModelConfigGetter,CharmHubClient) +// Source: github.com/juju/juju/cmd/juju/charmhub (interfaces: CharmHubClient) // Package mocks is a generated GoMock package. package mocks @@ -10,36 +10,35 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - charmhub "github.com/juju/juju/api/charmhub" - charmhub0 "github.com/juju/juju/charmhub" + charmhub "github.com/juju/juju/charmhub" transport "github.com/juju/juju/charmhub/transport" ) -// MockDownloadCommandAPI is a mock of DownloadCommandAPI interface -type MockDownloadCommandAPI struct { +// MockCharmHubClient is a mock of CharmHubClient interface. +type MockCharmHubClient struct { ctrl *gomock.Controller - recorder *MockDownloadCommandAPIMockRecorder + recorder *MockCharmHubClientMockRecorder } -// MockDownloadCommandAPIMockRecorder is the mock recorder for MockDownloadCommandAPI -type MockDownloadCommandAPIMockRecorder struct { - mock *MockDownloadCommandAPI +// MockCharmHubClientMockRecorder is the mock recorder for MockCharmHubClient. +type MockCharmHubClientMockRecorder struct { + mock *MockCharmHubClient } -// NewMockDownloadCommandAPI creates a new mock instance -func NewMockDownloadCommandAPI(ctrl *gomock.Controller) *MockDownloadCommandAPI { - mock := &MockDownloadCommandAPI{ctrl: ctrl} - mock.recorder = &MockDownloadCommandAPIMockRecorder{mock} +// NewMockCharmHubClient creates a new mock instance. +func NewMockCharmHubClient(ctrl *gomock.Controller) *MockCharmHubClient { + mock := &MockCharmHubClient{ctrl: ctrl} + mock.recorder = &MockCharmHubClientMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockDownloadCommandAPI) EXPECT() *MockDownloadCommandAPIMockRecorder { +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCharmHubClient) EXPECT() *MockCharmHubClientMockRecorder { return m.recorder } -// Download mocks base method -func (m *MockDownloadCommandAPI) Download(arg0 context.Context, arg1 *url.URL, arg2 string, arg3 ...charmhub0.DownloadOption) error { +// Download mocks base method. +func (m *MockCharmHubClient) Download(arg0 context.Context, arg1 *url.URL, arg2 string, arg3 ...charmhub.DownloadOption) error { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1, arg2} for _, a := range arg3 { @@ -50,300 +49,78 @@ func (m *MockDownloadCommandAPI) Download(arg0 context.Context, arg1 *url.URL, a return ret0 } -// Download indicates an expected call of Download -func (mr *MockDownloadCommandAPIMockRecorder) Download(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { +// Download indicates an expected call of Download. +func (mr *MockCharmHubClientMockRecorder) Download(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Download", reflect.TypeOf((*MockDownloadCommandAPI)(nil).Download), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Download", reflect.TypeOf((*MockCharmHubClient)(nil).Download), varargs...) } -// Info mocks base method -func (m *MockDownloadCommandAPI) Info(arg0 context.Context, arg1 string, arg2 ...charmhub0.InfoOption) (transport.InfoResponse, error) { +// Find mocks base method. +func (m *MockCharmHubClient) Find(arg0 context.Context, arg1 string, arg2 ...charmhub.FindOption) ([]transport.FindResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "Info", varargs...) - ret0, _ := ret[0].(transport.InfoResponse) + ret := m.ctrl.Call(m, "Find", varargs...) + ret0, _ := ret[0].([]transport.FindResponse) ret1, _ := ret[1].(error) return ret0, ret1 } -// Info indicates an expected call of Info -func (mr *MockDownloadCommandAPIMockRecorder) Info(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +// Find indicates an expected call of Find. +func (mr *MockCharmHubClientMockRecorder) Find(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockDownloadCommandAPI)(nil).Info), varargs...) -} - -// Refresh mocks base method -func (m *MockDownloadCommandAPI) Refresh(arg0 context.Context, arg1 charmhub0.RefreshConfig) ([]transport.RefreshResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Refresh", arg0, arg1) - ret0, _ := ret[0].([]transport.RefreshResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Refresh indicates an expected call of Refresh -func (mr *MockDownloadCommandAPIMockRecorder) Refresh(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockDownloadCommandAPI)(nil).Refresh), arg0, arg1) -} - -// MockInfoCommandAPI is a mock of InfoCommandAPI interface -type MockInfoCommandAPI struct { - ctrl *gomock.Controller - recorder *MockInfoCommandAPIMockRecorder -} - -// MockInfoCommandAPIMockRecorder is the mock recorder for MockInfoCommandAPI -type MockInfoCommandAPIMockRecorder struct { - mock *MockInfoCommandAPI -} - -// NewMockInfoCommandAPI creates a new mock instance -func NewMockInfoCommandAPI(ctrl *gomock.Controller) *MockInfoCommandAPI { - mock := &MockInfoCommandAPI{ctrl: ctrl} - mock.recorder = &MockInfoCommandAPIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockInfoCommandAPI) EXPECT() *MockInfoCommandAPIMockRecorder { - return m.recorder -} - -// Close mocks base method -func (m *MockInfoCommandAPI) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCharmHubClient)(nil).Find), varargs...) } -// Close indicates an expected call of Close -func (mr *MockInfoCommandAPIMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockInfoCommandAPI)(nil).Close)) -} - -// Info mocks base method -func (m *MockInfoCommandAPI) Info(arg0 string, arg1 ...charmhub.InfoOption) (charmhub.InfoResponse, error) { +// Info mocks base method. +func (m *MockCharmHubClient) Info(arg0 context.Context, arg1 string, arg2 ...charmhub.InfoOption) (transport.InfoResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Info", varargs...) - ret0, _ := ret[0].(charmhub.InfoResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Info indicates an expected call of Info -func (mr *MockInfoCommandAPIMockRecorder) Info(arg0 interface{}, arg1 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockInfoCommandAPI)(nil).Info), varargs...) -} - -// MockFindCommandAPI is a mock of FindCommandAPI interface -type MockFindCommandAPI struct { - ctrl *gomock.Controller - recorder *MockFindCommandAPIMockRecorder -} - -// MockFindCommandAPIMockRecorder is the mock recorder for MockFindCommandAPI -type MockFindCommandAPIMockRecorder struct { - mock *MockFindCommandAPI -} - -// NewMockFindCommandAPI creates a new mock instance -func NewMockFindCommandAPI(ctrl *gomock.Controller) *MockFindCommandAPI { - mock := &MockFindCommandAPI{ctrl: ctrl} - mock.recorder = &MockFindCommandAPIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockFindCommandAPI) EXPECT() *MockFindCommandAPIMockRecorder { - return m.recorder -} - -// Close mocks base method -func (m *MockFindCommandAPI) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close -func (mr *MockFindCommandAPIMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockFindCommandAPI)(nil).Close)) -} - -// Find mocks base method -func (m *MockFindCommandAPI) Find(arg0 string, arg1 ...charmhub.FindOption) ([]charmhub.FindResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Find", varargs...) - ret0, _ := ret[0].([]charmhub.FindResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find -func (mr *MockFindCommandAPIMockRecorder) Find(arg0 interface{}, arg1 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockFindCommandAPI)(nil).Find), varargs...) -} - -// MockModelConfigClient is a mock of ModelConfigClient interface -type MockModelConfigClient struct { - ctrl *gomock.Controller - recorder *MockModelConfigClientMockRecorder -} - -// MockModelConfigClientMockRecorder is the mock recorder for MockModelConfigClient -type MockModelConfigClientMockRecorder struct { - mock *MockModelConfigClient -} - -// NewMockModelConfigClient creates a new mock instance -func NewMockModelConfigClient(ctrl *gomock.Controller) *MockModelConfigClient { - mock := &MockModelConfigClient{ctrl: ctrl} - mock.recorder = &MockModelConfigClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockModelConfigClient) EXPECT() *MockModelConfigClientMockRecorder { - return m.recorder -} - -// Close mocks base method -func (m *MockModelConfigClient) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close -func (mr *MockModelConfigClientMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockModelConfigClient)(nil).Close)) -} - -// ModelGet mocks base method -func (m *MockModelConfigClient) ModelGet() (map[string]interface{}, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModelGet") - ret0, _ := ret[0].(map[string]interface{}) + ret0, _ := ret[0].(transport.InfoResponse) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModelGet indicates an expected call of ModelGet -func (mr *MockModelConfigClientMockRecorder) ModelGet() *gomock.Call { +// Info indicates an expected call of Info. +func (mr *MockCharmHubClientMockRecorder) Info(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModelGet", reflect.TypeOf((*MockModelConfigClient)(nil).ModelGet)) -} - -// MockModelConfigGetter is a mock of ModelConfigGetter interface -type MockModelConfigGetter struct { - ctrl *gomock.Controller - recorder *MockModelConfigGetterMockRecorder -} - -// MockModelConfigGetterMockRecorder is the mock recorder for MockModelConfigGetter -type MockModelConfigGetterMockRecorder struct { - mock *MockModelConfigGetter -} - -// NewMockModelConfigGetter creates a new mock instance -func NewMockModelConfigGetter(ctrl *gomock.Controller) *MockModelConfigGetter { - mock := &MockModelConfigGetter{ctrl: ctrl} - mock.recorder = &MockModelConfigGetterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockModelConfigGetter) EXPECT() *MockModelConfigGetterMockRecorder { - return m.recorder + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockCharmHubClient)(nil).Info), varargs...) } -// ModelGet mocks base method -func (m *MockModelConfigGetter) ModelGet() (map[string]interface{}, error) { +// Refresh mocks base method. +func (m *MockCharmHubClient) Refresh(arg0 context.Context, arg1 charmhub.RefreshConfig) ([]transport.RefreshResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModelGet") - ret0, _ := ret[0].(map[string]interface{}) + ret := m.ctrl.Call(m, "Refresh", arg0, arg1) + ret0, _ := ret[0].([]transport.RefreshResponse) ret1, _ := ret[1].(error) return ret0, ret1 } -// ModelGet indicates an expected call of ModelGet -func (mr *MockModelConfigGetterMockRecorder) ModelGet() *gomock.Call { +// Refresh indicates an expected call of Refresh. +func (mr *MockCharmHubClientMockRecorder) Refresh(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModelGet", reflect.TypeOf((*MockModelConfigGetter)(nil).ModelGet)) -} - -// MockCharmHubClient is a mock of CharmHubClient interface -type MockCharmHubClient struct { - ctrl *gomock.Controller - recorder *MockCharmHubClientMockRecorder -} - -// MockCharmHubClientMockRecorder is the mock recorder for MockCharmHubClient -type MockCharmHubClientMockRecorder struct { - mock *MockCharmHubClient -} - -// NewMockCharmHubClient creates a new mock instance -func NewMockCharmHubClient(ctrl *gomock.Controller) *MockCharmHubClient { - mock := &MockCharmHubClient{ctrl: ctrl} - mock.recorder = &MockCharmHubClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockCharmHubClient) EXPECT() *MockCharmHubClientMockRecorder { - return m.recorder + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockCharmHubClient)(nil).Refresh), arg0, arg1) } -// Download mocks base method -func (m *MockCharmHubClient) Download(arg0 context.Context, arg1 *url.URL, arg2 string) error { +// URL mocks base method. +func (m *MockCharmHubClient) URL() string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Download", arg0, arg1, arg2) - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "URL") + ret0, _ := ret[0].(string) return ret0 } -// Download indicates an expected call of Download -func (mr *MockCharmHubClientMockRecorder) Download(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Download", reflect.TypeOf((*MockCharmHubClient)(nil).Download), arg0, arg1, arg2) -} - -// Refresh mocks base method -func (m *MockCharmHubClient) Refresh(arg0 context.Context, arg1 charmhub0.RefreshConfig) ([]transport.RefreshResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Refresh", arg0, arg1) - ret0, _ := ret[0].([]transport.RefreshResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Refresh indicates an expected call of Refresh -func (mr *MockCharmHubClientMockRecorder) Refresh(arg0, arg1 interface{}) *gomock.Call { +// URL indicates an expected call of URL. +func (mr *MockCharmHubClientMockRecorder) URL() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockCharmHubClient)(nil).Refresh), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "URL", reflect.TypeOf((*MockCharmHubClient)(nil).URL)) } diff --git a/cmd/juju/charmhub/mocks/fsys_mock.go b/cmd/juju/charmhub/mocks/fsys_mock.go index 4ac423a16ff..d61b178849a 100644 --- a/cmd/juju/charmhub/mocks/fsys_mock.go +++ b/cmd/juju/charmhub/mocks/fsys_mock.go @@ -5,36 +5,38 @@ package mocks import ( - gomock "github.com/golang/mock/gomock" - modelcmd "github.com/juju/juju/cmd/modelcmd" + fs "io/fs" os "os" reflect "reflect" + + gomock "github.com/golang/mock/gomock" + modelcmd "github.com/juju/juju/cmd/modelcmd" ) -// MockFilesystem is a mock of Filesystem interface +// MockFilesystem is a mock of Filesystem interface. type MockFilesystem struct { ctrl *gomock.Controller recorder *MockFilesystemMockRecorder } -// MockFilesystemMockRecorder is the mock recorder for MockFilesystem +// MockFilesystemMockRecorder is the mock recorder for MockFilesystem. type MockFilesystemMockRecorder struct { mock *MockFilesystem } -// NewMockFilesystem creates a new mock instance +// NewMockFilesystem creates a new mock instance. func NewMockFilesystem(ctrl *gomock.Controller) *MockFilesystem { mock := &MockFilesystem{ctrl: ctrl} mock.recorder = &MockFilesystemMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFilesystem) EXPECT() *MockFilesystemMockRecorder { return m.recorder } -// Create mocks base method +// Create mocks base method. func (m *MockFilesystem) Create(arg0 string) (*os.File, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Create", arg0) @@ -43,13 +45,13 @@ func (m *MockFilesystem) Create(arg0 string) (*os.File, error) { return ret0, ret1 } -// Create indicates an expected call of Create +// Create indicates an expected call of Create. func (mr *MockFilesystemMockRecorder) Create(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockFilesystem)(nil).Create), arg0) } -// Open mocks base method +// Open mocks base method. func (m *MockFilesystem) Open(arg0 string) (modelcmd.ReadSeekCloser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Open", arg0) @@ -58,14 +60,14 @@ func (m *MockFilesystem) Open(arg0 string) (modelcmd.ReadSeekCloser, error) { return ret0, ret1 } -// Open indicates an expected call of Open +// Open indicates an expected call of Open. func (mr *MockFilesystemMockRecorder) Open(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockFilesystem)(nil).Open), arg0) } -// OpenFile mocks base method -func (m *MockFilesystem) OpenFile(arg0 string, arg1 int, arg2 os.FileMode) (*os.File, error) { +// OpenFile mocks base method. +func (m *MockFilesystem) OpenFile(arg0 string, arg1 int, arg2 fs.FileMode) (*os.File, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenFile", arg0, arg1, arg2) ret0, _ := ret[0].(*os.File) @@ -73,13 +75,13 @@ func (m *MockFilesystem) OpenFile(arg0 string, arg1 int, arg2 os.FileMode) (*os. return ret0, ret1 } -// OpenFile indicates an expected call of OpenFile +// OpenFile indicates an expected call of OpenFile. func (mr *MockFilesystemMockRecorder) OpenFile(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenFile", reflect.TypeOf((*MockFilesystem)(nil).OpenFile), arg0, arg1, arg2) } -// RemoveAll mocks base method +// RemoveAll mocks base method. func (m *MockFilesystem) RemoveAll(arg0 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveAll", arg0) @@ -87,51 +89,51 @@ func (m *MockFilesystem) RemoveAll(arg0 string) error { return ret0 } -// RemoveAll indicates an expected call of RemoveAll +// RemoveAll indicates an expected call of RemoveAll. func (mr *MockFilesystemMockRecorder) RemoveAll(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveAll", reflect.TypeOf((*MockFilesystem)(nil).RemoveAll), arg0) } -// Stat mocks base method -func (m *MockFilesystem) Stat(arg0 string) (os.FileInfo, error) { +// Stat mocks base method. +func (m *MockFilesystem) Stat(arg0 string) (fs.FileInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stat", arg0) - ret0, _ := ret[0].(os.FileInfo) + ret0, _ := ret[0].(fs.FileInfo) ret1, _ := ret[1].(error) return ret0, ret1 } -// Stat indicates an expected call of Stat +// Stat indicates an expected call of Stat. func (mr *MockFilesystemMockRecorder) Stat(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockFilesystem)(nil).Stat), arg0) } -// MockReadSeekCloser is a mock of ReadSeekCloser interface +// MockReadSeekCloser is a mock of ReadSeekCloser interface. type MockReadSeekCloser struct { ctrl *gomock.Controller recorder *MockReadSeekCloserMockRecorder } -// MockReadSeekCloserMockRecorder is the mock recorder for MockReadSeekCloser +// MockReadSeekCloserMockRecorder is the mock recorder for MockReadSeekCloser. type MockReadSeekCloserMockRecorder struct { mock *MockReadSeekCloser } -// NewMockReadSeekCloser creates a new mock instance +// NewMockReadSeekCloser creates a new mock instance. func NewMockReadSeekCloser(ctrl *gomock.Controller) *MockReadSeekCloser { mock := &MockReadSeekCloser{ctrl: ctrl} mock.recorder = &MockReadSeekCloserMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReadSeekCloser) EXPECT() *MockReadSeekCloserMockRecorder { return m.recorder } -// Close mocks base method +// Close mocks base method. func (m *MockReadSeekCloser) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") @@ -139,13 +141,13 @@ func (m *MockReadSeekCloser) Close() error { return ret0 } -// Close indicates an expected call of Close +// Close indicates an expected call of Close. func (mr *MockReadSeekCloserMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockReadSeekCloser)(nil).Close)) } -// Read mocks base method +// Read mocks base method. func (m *MockReadSeekCloser) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) @@ -154,13 +156,13 @@ func (m *MockReadSeekCloser) Read(arg0 []byte) (int, error) { return ret0, ret1 } -// Read indicates an expected call of Read +// Read indicates an expected call of Read. func (mr *MockReadSeekCloserMockRecorder) Read(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReadSeekCloser)(nil).Read), arg0) } -// Seek mocks base method +// Seek mocks base method. func (m *MockReadSeekCloser) Seek(arg0 int64, arg1 int) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Seek", arg0, arg1) @@ -169,7 +171,7 @@ func (m *MockReadSeekCloser) Seek(arg0 int64, arg1 int) (int64, error) { return ret0, ret1 } -// Seek indicates an expected call of Seek +// Seek indicates an expected call of Seek. func (mr *MockReadSeekCloserMockRecorder) Seek(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seek", reflect.TypeOf((*MockReadSeekCloser)(nil).Seek), arg0, arg1) diff --git a/cmd/juju/charmhub/mocks/os_mock.go b/cmd/juju/charmhub/mocks/os_mock.go index 104867817d4..0fefc8de199 100644 --- a/cmd/juju/charmhub/mocks/os_mock.go +++ b/cmd/juju/charmhub/mocks/os_mock.go @@ -5,34 +5,35 @@ package mocks import ( - gomock "github.com/golang/mock/gomock" reflect "reflect" + + gomock "github.com/golang/mock/gomock" ) -// MockOSEnviron is a mock of OSEnviron interface +// MockOSEnviron is a mock of OSEnviron interface. type MockOSEnviron struct { ctrl *gomock.Controller recorder *MockOSEnvironMockRecorder } -// MockOSEnvironMockRecorder is the mock recorder for MockOSEnviron +// MockOSEnvironMockRecorder is the mock recorder for MockOSEnviron. type MockOSEnvironMockRecorder struct { mock *MockOSEnviron } -// NewMockOSEnviron creates a new mock instance +// NewMockOSEnviron creates a new mock instance. func NewMockOSEnviron(ctrl *gomock.Controller) *MockOSEnviron { mock := &MockOSEnviron{ctrl: ctrl} mock.recorder = &MockOSEnvironMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockOSEnviron) EXPECT() *MockOSEnvironMockRecorder { return m.recorder } -// Getenv mocks base method +// Getenv mocks base method. func (m *MockOSEnviron) Getenv(arg0 string) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Getenv", arg0) @@ -40,13 +41,13 @@ func (m *MockOSEnviron) Getenv(arg0 string) string { return ret0 } -// Getenv indicates an expected call of Getenv +// Getenv indicates an expected call of Getenv. func (mr *MockOSEnvironMockRecorder) Getenv(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Getenv", reflect.TypeOf((*MockOSEnviron)(nil).Getenv), arg0) } -// IsTerminal mocks base method +// IsTerminal mocks base method. func (m *MockOSEnviron) IsTerminal() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTerminal") @@ -54,7 +55,7 @@ func (m *MockOSEnviron) IsTerminal() bool { return ret0 } -// IsTerminal indicates an expected call of IsTerminal +// IsTerminal indicates an expected call of IsTerminal. func (mr *MockOSEnvironMockRecorder) IsTerminal() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTerminal", reflect.TypeOf((*MockOSEnviron)(nil).IsTerminal)) diff --git a/cmd/juju/charmhub/package_test.go b/cmd/juju/charmhub/package_test.go index ae5be4dd0cd..be8e58c21a5 100644 --- a/cmd/juju/charmhub/package_test.go +++ b/cmd/juju/charmhub/package_test.go @@ -12,7 +12,7 @@ import ( gc "gopkg.in/check.v1" ) -//go:generate go run github.com/golang/mock/mockgen -package mocks -destination ./mocks/api_mock.go github.com/juju/juju/cmd/juju/charmhub DownloadCommandAPI,InfoCommandAPI,FindCommandAPI,ModelConfigClient,ModelConfigGetter,CharmHubClient +//go:generate go run github.com/golang/mock/mockgen -package mocks -destination ./mocks/api_mock.go github.com/juju/juju/cmd/juju/charmhub CharmHubClient //go:generate go run github.com/golang/mock/mockgen -package mocks -destination ./mocks/os_mock.go github.com/juju/juju/cmd/juju/charmhub OSEnviron //go:generate go run github.com/golang/mock/mockgen -package mocks -destination ./mocks/fsys_mock.go github.com/juju/juju/cmd/modelcmd Filesystem,ReadSeekCloser diff --git a/cmd/juju/commands/repl.go b/cmd/juju/commands/repl.go index c61731f7ce5..c6bb249d783 100644 --- a/cmd/juju/commands/repl.go +++ b/cmd/juju/commands/repl.go @@ -46,7 +46,7 @@ Type "q" or ^D or ^C to quit. var ( quitCommands = set.NewStrings("q", "quit", "exit") - noControllerCommands = set.NewStrings("help", "bootstrap", "register") + noControllerCommands = set.NewStrings("help", "bootstrap", "register", "version") ) const ( diff --git a/cmd/juju/machine/add.go b/cmd/juju/machine/add.go index bcb5288532a..26590eb39d5 100644 --- a/cmd/juju/machine/add.go +++ b/cmd/juju/machine/add.go @@ -71,7 +71,7 @@ the MAAS provider to acquire a particular node by specifying its hostname. Manual provisioning Call add-machine with the address of a network-accessible computer to -allocate that machine to the model. You should connect +allocate that machine to the model. Manual provisioning is the process of installing Juju on an existing machine and bringing it under Juju's management. The Juju controller must be able to diff --git a/cmd/jujud/agent/bootstrap.go b/cmd/jujud/agent/bootstrap.go index 95b51293120..8eb1f7c46b1 100644 --- a/cmd/jujud/agent/bootstrap.go +++ b/cmd/jujud/agent/bootstrap.go @@ -389,7 +389,7 @@ func getAddressesForMongo( args instancecfg.StateInitializationParams, ) (network.ProviderAddresses, error) { if isCAAS { - return network.NewProviderAddresses("localhost"), nil + return network.NewMachineAddresses([]string{"localhost"}).AsProviderAddresses(), nil } instanceLister, ok := env.(environs.InstanceLister) diff --git a/cmd/jujud/agent/machine_test.go b/cmd/jujud/agent/machine_test.go index 90ce8e86a8a..2d8235c278e 100644 --- a/cmd/jujud/agent/machine_test.go +++ b/cmd/jujud/agent/machine_test.go @@ -44,6 +44,7 @@ import ( "github.com/juju/juju/agent" "github.com/juju/juju/api" + "github.com/juju/juju/api/base" apimachiner "github.com/juju/juju/api/machiner" "github.com/juju/juju/apiserver/params" "github.com/juju/juju/cloud" @@ -76,6 +77,7 @@ import ( jujuversion "github.com/juju/juju/version" jworker "github.com/juju/juju/worker" "github.com/juju/juju/worker/authenticationworker" + "github.com/juju/juju/worker/charmrevision" "github.com/juju/juju/worker/diskmanager" "github.com/juju/juju/worker/instancepoller" "github.com/juju/juju/worker/machiner" @@ -99,10 +101,18 @@ type MachineSuite struct { var _ = gc.Suite(&MachineSuite{}) +// noopRevisionUpdater creates a stub to prevent outbound requests to the +// charmhub store and the charmstore. As these are meant to be unit tests, we +// should strive to remove outbound calls to external services. +type noopRevisionUpdater struct{} + +func (noopRevisionUpdater) UpdateLatestRevisions() error { + return nil +} + func (s *MachineSuite) SetUpTest(c *gc.C) { s.ControllerConfigAttrs = map[string]interface{}{ controller.AuditingEnabled: true, - controller.CharmStoreURL: "staging.charmstore", } s.commonMachineSuite.SetUpTest(c) bootstrapRaft(c, s.DataDir()) @@ -1037,10 +1047,6 @@ func (s *MachineSuite) TestMachineAgentIgnoreAddressesContainer(c *gc.C) { func (s *MachineSuite) TestMachineWorkers(c *gc.C) { testing.PatchExecutableAsEchoArgs(c, s, "ovs-vsctl", 0) - s.ControllerConfigAttrs = map[string]interface{}{ - controller.AuditingEnabled: true, - controller.CharmStoreURL: "staging.charmstore", - } tracker := agenttest.NewEngineTracker() instrumented := TrackMachines(c, tracker, iaasMachineManifolds) @@ -1058,6 +1064,10 @@ func (s *MachineSuite) TestMachineWorkers(c *gc.C) { } func (s *MachineSuite) TestControllerModelWorkers(c *gc.C) { + s.PatchValue(&charmrevision.NewAPIFacade, func(base.APICaller) (charmrevision.Facade, error) { + return noopRevisionUpdater{}, nil + }) + uuid := s.BackingState.ModelUUID() tracker := agenttest.NewEngineTracker() @@ -1073,6 +1083,10 @@ func (s *MachineSuite) TestControllerModelWorkers(c *gc.C) { } func (s *MachineSuite) TestHostedModelWorkers(c *gc.C) { + s.PatchValue(&charmrevision.NewAPIFacade, func(base.APICaller) (charmrevision.Facade, error) { + return noopRevisionUpdater{}, nil + }) + // The dummy provider blows up in the face of multi-model // scenarios so patch in a minimal environs.Environ that's good // enough to allow the model workers to run. diff --git a/cmd/jujud/agent/util_test.go b/cmd/jujud/agent/util_test.go index cd7e365b0c1..b91ac9dbfcf 100644 --- a/cmd/jujud/agent/util_test.go +++ b/cmd/jujud/agent/util_test.go @@ -210,7 +210,7 @@ func (s *commonMachineSuite) setFakeMachineAddresses(c *gc.C, machine *state.Mac c.Assert(err, jc.ErrorIsNil) insts, err := s.Environ.Instances(context.NewEmptyCloudCallContext(), []instance.Id{instId}) c.Assert(err, jc.ErrorIsNil) - dummy.SetInstanceAddresses(insts[0], network.NewProviderAddresses("0.1.2.3")) + dummy.SetInstanceAddresses(insts[0], network.NewMachineAddresses([]string{"0.1.2.3"}).AsProviderAddresses()) } type mockLoopDeviceManager struct { diff --git a/cmd/modelcmd/base.go b/cmd/modelcmd/base.go index ddb918c8aa8..1f2d0ee933e 100644 --- a/cmd/modelcmd/base.go +++ b/cmd/modelcmd/base.go @@ -120,6 +120,7 @@ type ModelAPI interface { // an API connection. type CommandBase struct { cmd.CommandBase + FilesystemCommand cmdContext *cmd.Context apiContexts map[string]*apiContext modelAPI_ ModelAPI @@ -136,10 +137,6 @@ type CommandBase struct { // Embedded is true if this command is being run inside a controller. Embedded bool - - // filesystem provides access to os calls to access files. - // For embedded commands, methods will always return an error. - filesystem Filesystem } func (c *CommandBase) assertRunStarted() { diff --git a/cmd/modelcmd/filesystem.go b/cmd/modelcmd/filesystem.go index d2fe0ba9e8e..ffade3c29f5 100644 --- a/cmd/modelcmd/filesystem.go +++ b/cmd/modelcmd/filesystem.go @@ -73,15 +73,22 @@ func (restrictedFilesystem) Stat(string) (os.FileInfo, error) { return nil, notSupported } +// FilesystemCommand is embedded in commands that need access to the filesystem. +type FilesystemCommand struct { + // filesystem provides access to os calls to access files. + // For embedded commands, methods will always return an error. + filesystem Filesystem +} + // SetFilesystem sets the Filesystem instance on the command. -func (c *CommandBase) SetFilesystem(fs Filesystem) { +func (c *FilesystemCommand) SetFilesystem(fs Filesystem) { c.filesystem = fs } // Filesystem returns an instance that provides access to // the filesystem, either delegating to calling os functions // or functions which always return an error. -func (c *CommandBase) Filesystem() Filesystem { +func (c *FilesystemCommand) Filesystem() Filesystem { if c.filesystem == nil { c.filesystem = osFilesystem{} } diff --git a/container/broker/broker_test.go b/container/broker/broker_test.go index 7e726da50a1..e89b89a341e 100644 --- a/container/broker/broker_test.go +++ b/container/broker/broker_test.go @@ -104,13 +104,13 @@ var fakeInterfaceInfo = corenetwork.InterfaceInfo{ MACAddress: "aa:bb:cc:dd:ee:ff", InterfaceName: "dummy0", Addresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("0.1.2.3", corenetwork.WithCIDR("0.1.2.0/24")), + corenetwork.NewMachineAddress("0.1.2.3", corenetwork.WithCIDR("0.1.2.0/24")).AsProviderAddress(), }, - GatewayAddress: corenetwork.NewProviderAddress("0.1.2.1"), + GatewayAddress: corenetwork.NewMachineAddress("0.1.2.1").AsProviderAddress(), // Explicitly set only DNSServers, but not DNSSearchDomains to test this is // detected and the latter populated by parsing the fake resolv.conf created // by patchResolvConf(). See LP bug http://pad.lv/1575940 for more info. - DNSServers: corenetwork.NewProviderAddresses("ns1.dummy"), + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.dummy"}).AsProviderAddresses(), DNSSearchDomains: nil, } diff --git a/container/lxd/container.go b/container/lxd/container.go index fa1fb4dd4f6..ce0ce2eabf6 100644 --- a/container/lxd/container.go +++ b/container/lxd/container.go @@ -236,7 +236,7 @@ func (s *Server) ContainerAddresses(name string) ([]corenetwork.ProviderAddress, continue } for _, addr := range net.Addresses { - netAddr := corenetwork.NewProviderAddress(addr.Address) + netAddr := corenetwork.NewMachineAddress(addr.Address).AsProviderAddress() if netAddr.Scope == corenetwork.ScopeLinkLocal || netAddr.Scope == corenetwork.ScopeMachineLocal { logger.Tracef("ignoring address %q for container %q", addr, name) continue diff --git a/container/lxd/container_test.go b/container/lxd/container_test.go index d9f8fce70a4..eda9b5b9694 100644 --- a/container/lxd/container_test.go +++ b/container/lxd/container_test.go @@ -243,7 +243,7 @@ func (s *containerSuite) TestContainerAddresses(c *gc.C) { c.Assert(err, jc.ErrorIsNil) expected := []corenetwork.ProviderAddress{ - corenetwork.NewProviderAddress("10.0.8.173", corenetwork.WithScope(corenetwork.ScopeCloudLocal)), + corenetwork.NewMachineAddress("10.0.8.173", corenetwork.WithScope(corenetwork.ScopeCloudLocal)).AsProviderAddress(), } c.Check(addrs, gc.DeepEquals, expected) } diff --git a/container/lxd/manager_test.go b/container/lxd/manager_test.go index e79942fcc85..d5a5f68c27f 100644 --- a/container/lxd/manager_test.go +++ b/container/lxd/manager_test.go @@ -370,7 +370,7 @@ func (s *managerSuite) TestNetworkDevicesFromConfigWithParentDevice(c *gc.C) { InterfaceType: "ethernet", MACAddress: "aa:bb:cc:dd:ee:f0", Addresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("", corenetwork.WithCIDR("10.10.0.0/24")), + corenetwork.NewMachineAddress("", corenetwork.WithCIDR("10.10.0.0/24")).AsProviderAddress(), }, }} diff --git a/controller/config.go b/controller/config.go index a8371fb1a9f..f1215f457c1 100755 --- a/controller/config.go +++ b/controller/config.go @@ -46,7 +46,7 @@ const ( // properly. ControllerAPIPort = "controller-api-port" - // Canonical name for the controller + // ControllerName is the canonical name for the controller ControllerName = "controller-name" // AgentRateLimitMax is the maximum size of the token bucket used to @@ -337,7 +337,7 @@ const ( // non-synced-writes-to-raft-log value. It is set to false by default. DefaultNonSyncedWritesToRaftLog = false - // DefaultMigrationMinionMaxWait is the default value for + // DefaultMigrationMinionWaitMax is the default value for DefaultMigrationMinionWaitMax = "15m" ) diff --git a/core/network/address.go b/core/network/address.go index 3b9579eca56..7e4213b9403 100644 --- a/core/network/address.go +++ b/core/network/address.go @@ -287,6 +287,18 @@ func (a MachineAddress) ValueWithMask() (string, error) { return ipNet.String(), nil } +// AsProviderAddress is used to construct a ProviderAddress +// from a MachineAddress +func (a MachineAddress) AsProviderAddress(options ...func(mutator ProviderAddressMutator)) ProviderAddress { + addr := ProviderAddress{MachineAddress: a} + + for _, option := range options { + option(&addr) + } + + return addr +} + // NewMachineAddress creates a new MachineAddress, // applying any supplied options to the result. func NewMachineAddress(value string, options ...func(AddressMutator)) MachineAddress { @@ -307,6 +319,30 @@ func NewMachineAddress(value string, options ...func(AddressMutator)) MachineAdd return addr } +// MachineAddresses is a slice of MachineAddress +type MachineAddresses []MachineAddress + +// AsProviderAddresses is used to construct ProviderAddresses +// element-wise from MachineAddresses +func (as MachineAddresses) AsProviderAddresses(options ...func(mutator ProviderAddressMutator)) ProviderAddresses { + addrs := make(ProviderAddresses, len(as)) + for i, addr := range as { + addrs[i] = addr.AsProviderAddress(options...) + } + return addrs +} + +// NewMachineAddresses is a convenience function to create addresses +// from a variable number of string arguments, applying any supplied +// options to each address +func NewMachineAddresses(values []string, options ...func(AddressMutator)) MachineAddresses { + addrs := make(MachineAddresses, len(values)) + for i, value := range values { + addrs[i] = NewMachineAddress(value, options...) + } + return addrs +} + // deriveScope attempts to derive the network scope from an address' // type and value, returning the original network scope if no // deduction can be made. @@ -368,8 +404,24 @@ func isIPv6UniqueLocalAddress(addrType AddressType, ip net.IP) bool { // address resides. type ProviderAddress struct { MachineAddress - SpaceName SpaceName + + // SpaceName is the space in which this address resides + SpaceName SpaceName + + // ProviderSpaceID is the provider's ID for the space this address is in ProviderSpaceID Id + + // ProviderID is the ID of this address's provider + ProviderID Id + + // ProviderSubnetID is the provider's ID for the subnet this address is in + ProviderSubnetID Id + + // ProviderVLANID is the provider's ID for the VLAN this address is in + ProviderVLANID Id + + // VLANTag is the tag associated with this address's VLAN + VLANTag int } // GoString implements fmt.GoStringer. @@ -405,45 +457,10 @@ func (a ProviderAddress) String() string { return buf.String() } -// NewProviderAddress creates a new ProviderAddress, -// applying any supplied options to the result. -func NewProviderAddress(value string, options ...func(AddressMutator)) ProviderAddress { - return ProviderAddress{MachineAddress: NewMachineAddress(value, options...)} -} - -// NewProviderAddressInSpace creates a new ProviderAddress, -// associating it with the given space name. -func NewProviderAddressInSpace(spaceName string, value string, options ...func(AddressMutator)) ProviderAddress { - return ProviderAddress{ - MachineAddress: NewMachineAddress(value, options...), - SpaceName: SpaceName(spaceName), - } -} - // ProviderAddresses is a slice of ProviderAddress // supporting conversion to SpaceAddresses. type ProviderAddresses []ProviderAddress -// NewProviderAddresses is a convenience function to create addresses -// from a variable number of string arguments. -func NewProviderAddresses(inAddresses ...string) (outAddresses ProviderAddresses) { - outAddresses = make(ProviderAddresses, len(inAddresses)) - for i, address := range inAddresses { - outAddresses[i] = NewProviderAddress(address) - } - return outAddresses -} - -// NewProviderAddressesInSpace is a convenience function to create addresses -// in the same space, from a a variable number of string arguments. -func NewProviderAddressesInSpace(spaceName string, inAddresses ...string) (outAddresses ProviderAddresses) { - outAddresses = make(ProviderAddresses, len(inAddresses)) - for i, address := range inAddresses { - outAddresses[i] = NewProviderAddressInSpace(spaceName, address) - } - return outAddresses -} - // ToIPAddresses transforms the ProviderAddresses to a string slice containing // their raw IP values. func (pas ProviderAddresses) ToIPAddresses() []string { diff --git a/core/network/address_options.go b/core/network/address_options.go index 6da75f748da..1b4123defc1 100644 --- a/core/network/address_options.go +++ b/core/network/address_options.go @@ -72,3 +72,110 @@ func WithConfigType(configType AddressConfigType) func(AddressMutator) { a.SetConfigType(configType) } } + +// ProviderAddressMutator describes setter methods for a ProviderAddress +type ProviderAddressMutator interface { + AddressMutator + + // SetSpaceName sets the SpaceName property of the provider address + SetSpaceName(string) + + // SetProviderSpaceID sets the ProviderSpaceID property of the provider address + SetProviderSpaceID(Id) + + // SetProviderID sets the ProviderID property of the provider address + SetProviderID(Id) + + // SetProviderSubnetID sets the ProviderSubnetID property of the provider address + SetProviderSubnetID(Id) + + // SetProviderVLANID sets the ProviderVLANID property of the provider address + SetProviderVLANID(Id) + + // SetVLANTag sets the VLANTag property of the provider address + SetVLANTag(int) +} + +// SetSpaceName (ProviderAddressMutator) sets the input +// space name on the provider address receiver +func (a *ProviderAddress) SetSpaceName(spaceName string) { + a.SpaceName = SpaceName(spaceName) +} + +// SetProviderSpaceID (ProviderAddressMutator) sets the input +// provider space id on the provider address receiver +func (a *ProviderAddress) SetProviderSpaceID(id Id) { + a.ProviderSpaceID = id +} + +// SetProviderID (ProviderAddressMutator) sets the input +// provider id on the provider address receiver +func (a *ProviderAddress) SetProviderID(id Id) { + a.ProviderID = id +} + +// SetProviderSubnetID (ProviderAddressMutator) sets the input +// provider subnet id on the provider addrerss reviever +func (a *ProviderAddress) SetProviderSubnetID(id Id) { + a.ProviderSubnetID = id +} + +// SetProviderVLANID (ProviderAddressMutator) sets the input +// provider VLAN id on the provider addrerss reviever +func (a *ProviderAddress) SetProviderVLANID(id Id) { + a.ProviderVLANID = id +} + +// SetVLANTag (ProviderAddressMutator) sets the input +// VLAN tag on the provider addrerss reviever +func (a *ProviderAddress) SetVLANTag(tag int) { + a.VLANTag = tag +} + +// WithSpaceName returns a functional option that can +// be used to set the input space name on a provider address. +func WithSpaceName(space string) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetSpaceName(space) + } +} + +// WithProviderSpaceID returns a functional option that can +// be used to set the input provider space id on a provider address +func WithProviderSpaceID(id Id) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetProviderSpaceID(id) + } +} + +// WithProviderID returns a functional option that can +// be used to set the input provider id on a provider address +func WithProviderID(id Id) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetProviderID(id) + } +} + +// WithProviderSubnetID returns a functional option that can +// be used to set the input provider subnet id on a provider address +func WithProviderSubnetID(id Id) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetProviderSubnetID(id) + } +} + +// WithProviderVLANID returns a functional option that can +// be used to set the input provider VLAN id on a provider address +func WithProviderVLANID(id Id) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetProviderVLANID(id) + } +} + +// WithVLANTag returns a functional option that can +// be used to set the input VLAN tag on a provider address +func WithVLANTag(tag int) func(ProviderAddressMutator) { + return func(a ProviderAddressMutator) { + a.SetVLANTag(tag) + } +} diff --git a/core/network/address_test.go b/core/network/address_test.go index be56803d4b0..15b6dbc2db3 100644 --- a/core/network/address_test.go +++ b/core/network/address_test.go @@ -121,16 +121,30 @@ func (s *AddressSuite) TestNewScopedAddressIPv6(c *gc.C) { } } -func (s *AddressSuite) TestNewProviderAddressInSpace(c *gc.C) { - addr1 := network.NewProviderAddressInSpace("foo", "0.1.2.3") - addr2 := network.NewProviderAddressInSpace("", "2001:db8::123") +func (s *AddressSuite) TestAsProviderAddress(c *gc.C) { + addr1 := network.NewMachineAddress("0.1.2.3").AsProviderAddress( + network.WithSpaceName("foo"), + network.WithProviderSpaceID("3"), + network.WithProviderID("523"), + network.WithProviderSubnetID("5"), + network.WithProviderVLANID("5001"), + network.WithVLANTag(50), + ) + addr2 := network.NewMachineAddress("2001:db8::123").AsProviderAddress( + network.WithSpaceName(""), + ) c.Check(addr1, jc.DeepEquals, network.ProviderAddress{ MachineAddress: network.MachineAddress{ Value: "0.1.2.3", Type: "ipv4", Scope: "public", }, - SpaceName: "foo", + SpaceName: "foo", + ProviderSpaceID: "3", + ProviderID: "523", + ProviderSubnetID: "5", + ProviderVLANID: "5001", + VLANTag: 50, }) c.Check(addr2, jc.DeepEquals, network.ProviderAddress{ MachineAddress: network.MachineAddress{ @@ -142,22 +156,36 @@ func (s *AddressSuite) TestNewProviderAddressInSpace(c *gc.C) { }) } -func (s *AddressSuite) TestNewProviderAddressesInSpace(c *gc.C) { - addrs := network.NewProviderAddressesInSpace("bar", "0.2.3.4", "fc00::1") +func (s *AddressSuite) TestAsProviderAddresses(c *gc.C) { + addrs := network.NewMachineAddresses([]string{"0.2.3.4", "fc00::1"}).AsProviderAddresses( + network.WithSpaceName("bar"), + network.WithProviderSpaceID("4"), + network.WithProviderSubnetID("6"), + network.WithProviderVLANID("5002"), + network.WithVLANTag(100), + ) c.Check(addrs, jc.DeepEquals, network.ProviderAddresses{{ MachineAddress: network.MachineAddress{ Value: "0.2.3.4", Type: "ipv4", Scope: "public", }, - SpaceName: "bar", + SpaceName: "bar", + ProviderSpaceID: "4", + ProviderSubnetID: "6", + ProviderVLANID: "5002", + VLANTag: 100, }, { MachineAddress: network.MachineAddress{ Value: "fc00::1", Type: "ipv6", Scope: "local-cloud", }, - SpaceName: "bar", + SpaceName: "bar", + ProviderSpaceID: "4", + ProviderSubnetID: "6", + ProviderVLANID: "5002", + VLANTag: 100, }}) } @@ -676,6 +704,54 @@ var stringTests = []struct { ProviderSpaceID: network.Id("3"), }, str: "public:foo.com@badlands(id:3)", +}, { + addr: network.ProviderAddress{ + MachineAddress: network.MachineAddress{ + Type: network.HostName, + Value: "foo.com", + Scope: network.ScopePublic, + }, + SpaceName: "badlands", + ProviderSpaceID: network.Id("3"), + ProviderID: "523", + }, + str: "public:foo.com@badlands(id:3)", +}, { + addr: network.ProviderAddress{ + MachineAddress: network.MachineAddress{ + Type: network.HostName, + Value: "foo.com", + Scope: network.ScopePublic, + }, + SpaceName: "badlands", + ProviderSpaceID: network.Id("3"), + ProviderSubnetID: "5", + }, + str: "public:foo.com@badlands(id:3)", +}, { + addr: network.ProviderAddress{ + MachineAddress: network.MachineAddress{ + Type: network.HostName, + Value: "foo.com", + Scope: network.ScopePublic, + }, + SpaceName: "badlands", + ProviderSpaceID: network.Id("3"), + ProviderVLANID: "5001", + }, + str: "public:foo.com@badlands(id:3)", +}, { + addr: network.ProviderAddress{ + MachineAddress: network.MachineAddress{ + Type: network.HostName, + Value: "foo.com", + Scope: network.ScopePublic, + }, + SpaceName: "badlands", + ProviderSpaceID: network.Id("3"), + VLANTag: 50, + }, + str: "public:foo.com@badlands(id:3)", }} func (s *AddressSuite) TestString(c *gc.C) { @@ -747,7 +823,7 @@ func (*AddressSuite) TestExactScopeMatch(c *gc.C) { match = network.ExactScopeMatch(addr, network.ScopePublic) c.Assert(match, jc.IsFalse) - addr = network.NewProviderAddress("8.8.8.8", network.WithScope(network.ScopePublic)) + addr = network.NewMachineAddress("8.8.8.8", network.WithScope(network.ScopePublic)).AsProviderAddress() match = network.ExactScopeMatch(addr, network.ScopeCloudLocal) c.Assert(match, jc.IsFalse) match = network.ExactScopeMatch(addr, network.ScopePublic) @@ -810,9 +886,9 @@ func (s stubLookup) AllSpaceInfos() (network.SpaceInfos, error) { func (s *AddressSuite) TestProviderAddressesToSpaceAddresses(c *gc.C) { // Check success. addrs := network.ProviderAddresses{ - network.NewProviderAddressInSpace("space-one", "1.2.3.4"), - network.NewProviderAddressInSpace("space-two", "2.3.4.5"), - network.NewProviderAddress("3.4.5.6"), + network.NewMachineAddress("1.2.3.4").AsProviderAddress(network.WithSpaceName("space-one")), + network.NewMachineAddress("2.3.4.5").AsProviderAddress(network.WithSpaceName("space-two")), + network.NewMachineAddress("3.4.5.6").AsProviderAddress(), } exp := network.NewSpaceAddresses("1.2.3.4", "2.3.4.5", "3.4.5.6") @@ -824,7 +900,7 @@ func (s *AddressSuite) TestProviderAddressesToSpaceAddresses(c *gc.C) { c.Check(res, jc.SameContents, exp) // Add an address in a space that the lookup will not resolve. - addrs = append(addrs, network.NewProviderAddressInSpace("space-denied", "4.5.6.7")) + addrs = append(addrs, network.NewMachineAddress("4.5.6.7").AsProviderAddress(network.WithSpaceName("space-denied"))) _, err = addrs.ToSpaceAddresses(stubLookup{}) c.Assert(err, jc.Satisfies, errors.IsNotFound) } @@ -836,9 +912,9 @@ func (s *AddressSuite) TestSpaceAddressesToProviderAddresses(c *gc.C) { addrs[1].SpaceID = "2" exp := network.ProviderAddresses{ - network.NewProviderAddressInSpace("space-one", "1.2.3.4"), - network.NewProviderAddressInSpace("space-two", "2.3.4.5"), - network.NewProviderAddress("3.4.5.6"), + network.NewMachineAddress("1.2.3.4").AsProviderAddress(network.WithSpaceName("space-one")), + network.NewMachineAddress("2.3.4.5").AsProviderAddress(network.WithSpaceName("space-two")), + network.NewMachineAddress("3.4.5.6").AsProviderAddress(), } // Only the first address in the lookup has a provider ID. exp[0].ProviderSpaceID = "p1" diff --git a/core/network/dns.go b/core/network/dns.go index 76b04b14acc..f1a2966ded7 100644 --- a/core/network/dns.go +++ b/core/network/dns.go @@ -84,7 +84,7 @@ func ParseResolvConf(path string) (*DNSConfig, error) { } return &DNSConfig{ - Nameservers: NewProviderAddresses(nameservers...), + Nameservers: NewMachineAddresses(nameservers).AsProviderAddresses(), SearchDomains: searchDomains, }, nil } diff --git a/core/network/dns_test.go b/core/network/dns_test.go index 77dbc605c9d..5f6251a4eb6 100644 --- a/core/network/dns_test.go +++ b/core/network/dns_test.go @@ -73,7 +73,7 @@ nameserver 8.8.8.8 #comment #still the same comment result, err := network.ParseResolvConf(fakeConf) c.Check(err, jc.ErrorIsNil) c.Check(result, jc.DeepEquals, &network.DNSConfig{ - Nameservers: network.NewProviderAddresses("8.8.8.8"), + Nameservers: network.NewMachineAddresses([]string{"8.8.8.8"}).AsProviderAddresses(), SearchDomains: []string{"foo", "example.com", "bar."}, }) } diff --git a/core/network/hostport_test.go b/core/network/hostport_test.go index 8dc3fd2f8a2..f8cf567ac8d 100644 --- a/core/network/hostport_test.go +++ b/core/network/hostport_test.go @@ -151,16 +151,16 @@ func (*HostPortSuite) TestParseProviderHostPortsSuccess(c *gc.C) { expect: []network.ProviderHostPort{}, }, { args: []string{"1.2.3.4:42"}, - expect: []network.ProviderHostPort{{network.NewProviderAddress("1.2.3.4"), 42}}, + expect: []network.ProviderHostPort{{network.NewMachineAddress("1.2.3.4").AsProviderAddress(), 42}}, }, { args: []string{"[fc00::1]:1234"}, - expect: []network.ProviderHostPort{{network.NewProviderAddress("fc00::1"), 1234}}, + expect: []network.ProviderHostPort{{network.NewMachineAddress("fc00::1").AsProviderAddress(), 1234}}, }, { args: []string{"[fc00::1]:1234", "127.0.0.1:4321", "example.com:42"}, expect: []network.ProviderHostPort{ - {network.NewProviderAddress("fc00::1"), 1234}, - {network.NewProviderAddress("127.0.0.1"), 4321}, - {network.NewProviderAddress("example.com"), 42}, + {network.NewMachineAddress("fc00::1").AsProviderAddress(), 1234}, + {network.NewMachineAddress("127.0.0.1").AsProviderAddress(), 4321}, + {network.NewMachineAddress("example.com").AsProviderAddress(), 42}, }, }} { c.Logf("test %d: args %v", i, test.args) @@ -515,15 +515,15 @@ func (s *HostPortSuite) TestSpaceHostPortsToProviderHostPorts(c *gc.C) { exp := network.ProviderHostPorts{ { - ProviderAddress: network.NewProviderAddressInSpace("space-one", "1.2.3.4"), + ProviderAddress: network.NewMachineAddress("1.2.3.4").AsProviderAddress(network.WithSpaceName("space-one")), NetPort: 1234, }, { - ProviderAddress: network.NewProviderAddressInSpace("space-two", "2.3.4.5"), + ProviderAddress: network.NewMachineAddress("2.3.4.5").AsProviderAddress(network.WithSpaceName("space-two")), NetPort: 1234, }, { - ProviderAddress: network.NewProviderAddress("3.4.5.6"), + ProviderAddress: network.NewMachineAddress("3.4.5.6").AsProviderAddress(), NetPort: 1234, }, } diff --git a/core/network/nic_test.go b/core/network/nic_test.go index df8c05ea412..da4350c0665 100644 --- a/core/network/nic_test.go +++ b/core/network/nic_test.go @@ -25,9 +25,9 @@ func (s *nicSuite) SetUpTest(_ *gc.C) { {VLANTag: 0, DeviceIndex: 1, InterfaceName: "eth1"}, {VLANTag: 42, DeviceIndex: 2, InterfaceName: "br2"}, {ConfigType: network.ConfigDHCP, NoAutoStart: true}, - {Addresses: network.ProviderAddresses{network.NewProviderAddress("0.1.2.3")}}, - {DNSServers: network.NewProviderAddresses("1.1.1.1", "2.2.2.2")}, - {GatewayAddress: network.NewProviderAddress("4.3.2.1")}, + {Addresses: network.ProviderAddresses{network.NewMachineAddress("0.1.2.3").AsProviderAddress()}}, + {DNSServers: network.NewMachineAddresses([]string{"1.1.1.1", "2.2.2.2"}).AsProviderAddresses()}, + {GatewayAddress: network.NewMachineAddress("4.3.2.1").AsProviderAddress()}, {AvailabilityZones: []string{"foo", "bar"}}, {Routes: []network.Route{{ DestinationCIDR: "0.1.2.3/24", @@ -60,9 +60,9 @@ func (s *nicSuite) TestIsVLAN(c *gc.C) { func (s *nicSuite) TestAdditionalFields(c *gc.C) { c.Check(s.info[3].ConfigType, gc.Equals, network.ConfigDHCP) c.Check(s.info[3].NoAutoStart, jc.IsTrue) - c.Check(s.info[4].Addresses, jc.DeepEquals, network.ProviderAddresses{network.NewProviderAddress("0.1.2.3")}) - c.Check(s.info[5].DNSServers, jc.DeepEquals, network.NewProviderAddresses("1.1.1.1", "2.2.2.2")) - c.Check(s.info[6].GatewayAddress, jc.DeepEquals, network.NewProviderAddress("4.3.2.1")) + c.Check(s.info[4].Addresses, jc.DeepEquals, network.ProviderAddresses{network.NewMachineAddress("0.1.2.3").AsProviderAddress()}) + c.Check(s.info[5].DNSServers, jc.DeepEquals, network.NewMachineAddresses([]string{"1.1.1.1", "2.2.2.2"}).AsProviderAddresses()) + c.Check(s.info[6].GatewayAddress, jc.DeepEquals, network.NewMachineAddress("4.3.2.1").AsProviderAddress()) c.Check(s.info[7].AvailabilityZones, jc.DeepEquals, []string{"foo", "bar"}) c.Check(s.info[8].Routes, jc.DeepEquals, []network.Route{{ DestinationCIDR: "0.1.2.3/24", diff --git a/core/raft/queue/opqueue_test.go b/core/raft/queue/opqueue_test.go index 3495c4e3b41..a1d94f9edee 100644 --- a/core/raft/queue/opqueue_test.go +++ b/core/raft/queue/opqueue_test.go @@ -9,12 +9,13 @@ import ( "sync" "time" - "github.com/juju/clock" - "github.com/juju/juju/core/raftlease" + "github.com/juju/clock/testclock" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" "gopkg.in/yaml.v3" + + "github.com/juju/juju/core/raftlease" ) type OpQueueSuite struct { @@ -23,8 +24,8 @@ type OpQueueSuite struct { var _ = gc.Suite(&OpQueueSuite{}) -func (s *OpQueueSuite) TestEnqueue(c *gc.C) { - queue := NewOpQueue(clock.WallClock) +func (s *OpQueueSuite) TestEnqueueDequeue(c *gc.C) { + queue := NewOpQueue(testclock.NewClock(time.Now())) results := consumeN(c, queue, 1) @@ -56,7 +57,7 @@ func (s *OpQueueSuite) TestEnqueue(c *gc.C) { } func (s *OpQueueSuite) TestEnqueueWithStopped(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) canceled := make(chan struct{}, 1) close(canceled) @@ -86,7 +87,7 @@ func (s *OpQueueSuite) TestEnqueueWithStopped(c *gc.C) { } func (s *OpQueueSuite) TestEnqueueWithError(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) results := consumeNUntilErr(c, queue, 1, errors.New("boom")) @@ -117,12 +118,13 @@ func (s *OpQueueSuite) TestEnqueueWithError(c *gc.C) { c.Assert(count, gc.Equals, 1) } -func (s *OpQueueSuite) TestSynchronousEnqueueSingleDispatch(c *gc.C) { - queue := NewOpQueue(clock.WallClock) +func (s *OpQueueSuite) TestSynchronousEnqueueImmediateDispatch(c *gc.C) { + queue := NewOpQueue(testclock.NewClock(time.Now())) - toEnqueue := 3 + toEnqueue := 5 go func() { - // Synchronous enqueues will result in batches of 1. + // Synchronous enqueues should result in multiple batches despite + // being fewer total ops than the maximum batch size. for i := 0; i < toEnqueue; i++ { err := queue.Enqueue(InOperation{ Command: command(opName(i)), @@ -145,19 +147,19 @@ func (s *OpQueueSuite) TestSynchronousEnqueueSingleDispatch(c *gc.C) { err := queue.Wait() c.Assert(err, jc.ErrorIsNil) - // The 3 operations were dispatched in separate batches. - c.Assert(len(results), gc.Equals, 3) + c.Assert(len(results) > 1, jc.IsTrue) } func (s *OpQueueSuite) TestConcurrentEnqueueMultiDispatch(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) toEnqueue := EnqueueBatchSize * 3 for i := 0; i < toEnqueue; i++ { go func(i int) { - queue.Enqueue(InOperation{ + err := queue.Enqueue(InOperation{ Command: command(opName(i)), }) + c.Assert(err, jc.ErrorIsNil) }(i) } @@ -176,13 +178,14 @@ func (s *OpQueueSuite) TestConcurrentEnqueueMultiDispatch(c *gc.C) { c.Assert(err, jc.ErrorIsNil) // The exact batching that occurs is variable, but as a conservative test, - // ensure that we had some factor of batching. + // ensure that we had a decent factor of batching - the number of batches + // is fewer than 1/3 of the total enqueued operations. c.Check(len(results) > 1, jc.IsTrue) c.Check(len(results) < EnqueueBatchSize, jc.IsTrue) } func (s *OpQueueSuite) TestMultipleEnqueueWithErrors(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) results := make(chan raftlease.Command, 3) go func() { @@ -262,7 +265,7 @@ func (s *OpQueueSuite) TestMultipleEnqueueWithErrors(c *gc.C) { } func (s *OpQueueSuite) TestMultipleEnqueueWithStop(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) results := make(chan raftlease.Command, 2) go func() { @@ -346,7 +349,7 @@ func (s *OpQueueSuite) TestMultipleEnqueueWithStop(c *gc.C) { } func (s *OpQueueSuite) TestMultipleEnqueues(c *gc.C) { - queue := NewOpQueue(clock.WallClock) + queue := NewOpQueue(testclock.NewClock(time.Now())) results := consumeN(c, queue, 10) diff --git a/environs/manual/addresses_test.go b/environs/manual/addresses_test.go index 85e0d0b06e2..5cfba56b318 100644 --- a/environs/manual/addresses_test.go +++ b/environs/manual/addresses_test.go @@ -42,7 +42,7 @@ func (s *addressesSuite) TestHostAddress(c *gc.C) { addr, err := manual.HostAddress(validHost) c.Assert(s.netLookupHostCalled, gc.Equals, 1) c.Assert(err, jc.ErrorIsNil) - c.Assert(addr, gc.Equals, network.NewProviderAddress(validHost, network.WithScope(network.ScopePublic))) + c.Assert(addr, gc.Equals, network.NewMachineAddress(validHost, network.WithScope(network.ScopePublic)).AsProviderAddress()) } func (s *addressesSuite) TestHostAddressError(c *gc.C) { @@ -56,12 +56,12 @@ func (s *addressesSuite) TestHostAddressIPv4(c *gc.C) { addr, err := manual.HostAddress("127.0.0.1") c.Assert(s.netLookupHostCalled, gc.Equals, 0) c.Assert(err, jc.ErrorIsNil) - c.Assert(addr, gc.Equals, network.NewProviderAddress("127.0.0.1", network.WithScope(network.ScopePublic))) + c.Assert(addr, gc.Equals, network.NewMachineAddress("127.0.0.1", network.WithScope(network.ScopePublic)).AsProviderAddress()) } func (s *addressesSuite) TestHostAddressIPv6(c *gc.C) { addr, err := manual.HostAddress("::1") c.Assert(s.netLookupHostCalled, gc.Equals, 0) c.Assert(err, jc.ErrorIsNil) - c.Assert(addr, gc.Equals, network.NewProviderAddress("::1", network.WithScope(network.ScopePublic))) + c.Assert(addr, gc.Equals, network.NewMachineAddress("::1", network.WithScope(network.ScopePublic)).AsProviderAddress()) } diff --git a/go.mod b/go.mod index 346716dca35..0657618ca4b 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/iam v1.9.0 github.com/aws/smithy-go v1.8.0 github.com/bmizerany/pat v0.0.0-20160217103242-c068ca2f0aac - github.com/canonical/pebble v0.0.0-20211208232418-4d9bc7c18005 + github.com/canonical/pebble v0.0.0-20220105032148-3a48156fcbf0 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/coreos/go-systemd/v22 v22.0.0-20200316104309-cb8b64719ae3 github.com/dnaeon/go-vcr v1.1.0 // indirect @@ -65,7 +65,7 @@ require ( github.com/juju/jsonschema v0.0.0-20210422141032-b0ff291abe9c github.com/juju/jsonschema-gen v0.0.0-20200416014454-d924343d72b2 github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 - github.com/juju/mgo/v2 v2.0.0-20210414025616-e854c672032f + github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090 github.com/juju/mutex v0.0.0-20180619145857-d21b13acf4bf github.com/juju/names/v4 v4.0.0-20200929085019-be23e191fee0 github.com/juju/naturalsort v0.0.0-20180423034842-5b81707e882b @@ -201,6 +201,7 @@ require ( github.com/prometheus/procfs v0.1.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect + github.com/xdg-go/stringprep v1.0.2 // indirect go.etcd.io/bbolt v1.3.5 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect golang.org/x/text v0.3.6 // indirect diff --git a/go.sum b/go.sum index 372ee2153dd..e49680483c3 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/pat v0.0.0-20160217103242-c068ca2f0aac h1:X5YRFJiteUM3rajABEYJSzw1KWgmp1ulPFKxpfLm0M4= github.com/bmizerany/pat v0.0.0-20160217103242-c068ca2f0aac/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/canonical/pebble v0.0.0-20211208232418-4d9bc7c18005 h1:gb+1Krh3y40cJdrzzi2WJKuHgIr/q3CGBbGXIEJuQ3g= -github.com/canonical/pebble v0.0.0-20211208232418-4d9bc7c18005/go.mod h1:+0rQ57rhB9pciKKaE/QlwPL4R8mujv+24D81KGYRlV0= +github.com/canonical/pebble v0.0.0-20220105032148-3a48156fcbf0 h1:Bzr3QOteZ+bPUxFGjgA3jkYnERpeFfbgGaLsH9g7qm8= +github.com/canonical/pebble v0.0.0-20220105032148-3a48156fcbf0/go.mod h1:+0rQ57rhB9pciKKaE/QlwPL4R8mujv+24D81KGYRlV0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -480,8 +480,8 @@ github.com/juju/lumberjack v2.0.0-20200420012306-ddfd864a6ade+incompatible h1:7L github.com/juju/lumberjack v2.0.0-20200420012306-ddfd864a6ade+incompatible/go.mod h1:YQBneJkXlAAye6yHFYH8CabVID+0Oq2by8DDKGj5OIU= github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f/go.mod h1:+7K7MqWi5xWI+s1LyB2g0Di71jZo27y+XOlmhNtV1Y0= github.com/juju/mgo/v2 v2.0.0-20210302023703-70d5d206e208/go.mod h1:0OChplkvPTZ174D2FYZXg4IB9hbEwyHkD+zT+/eK+Fg= -github.com/juju/mgo/v2 v2.0.0-20210414025616-e854c672032f h1:/Wj+9vztEkhkudRz596GbOu3x6FmftHk2vurf4yaaxw= -github.com/juju/mgo/v2 v2.0.0-20210414025616-e854c672032f/go.mod h1:0OChplkvPTZ174D2FYZXg4IB9hbEwyHkD+zT+/eK+Fg= +github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090 h1:zX5GoH3Jp8k1EjUFkApu/YZAYEn0PYQfg/U6IDyNyYs= +github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090/go.mod h1:N614SE0a4e+ih2rg96Vi2PeC3cTpUOWgCTv3Cgk974c= github.com/juju/mgomonitor v0.0.0-20181029151116-52206bb0cd31/go.mod h1:m6E+J+I+cE+6rcaVxSI4HwGLIEOCSOBMYedt3Sewh+U= github.com/juju/mgotest v1.0.1/go.mod h1:vTaDufYul+Ps8D7bgseHjq87X8eu0ivlKLp9mVc/Bfc= github.com/juju/mgotest v1.0.2 h1:rgeY0zbfWvxsuCz9m13VAGPFQVzQJeSZOnJ/AzkrkRQ= @@ -812,6 +812,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vmware/govmomi v0.21.1-0.20191008161538-40aebf13ba45 h1:zpQBW+l4uPQTfTOxedN5GEcSONhabbCf3X+5+P/H4Jk= github.com/vmware/govmomi v0.21.1-0.20191008161538-40aebf13ba45/go.mod h1:zbnFoBQ9GIjs2RVETy8CNEpb+L+Lwkjs3XZUL0B3/m0= github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk= +github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -1016,6 +1018,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/juju/api_test.go b/juju/api_test.go index f1a6307f672..f96f2e86762 100644 --- a/juju/api_test.go +++ b/juju/api_test.go @@ -287,8 +287,8 @@ func (s *NewAPIClientSuite) TestDialedAddressIsCached(c *gc.C) { DialWebsocket: func(ctx context.Context, urlStr string, tlsConfig *tls.Config, ipAddr string) (jsoncodec.JSONConn, error) { apiConn := testRootAPI{ serverAddrs: params.FromProviderHostsPorts([]network.ProviderHostPorts{{ - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("example3"), NetPort: 3333}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("example4"), NetPort: 4444}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("example3").AsProviderAddress(), NetPort: 3333}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("example4").AsProviderAddress(), NetPort: 4444}, }}), } dialed <- ipAddr @@ -349,8 +349,8 @@ func (s *NewAPIClientSuite) TestWithExistingDNSCache(c *gc.C) { DialWebsocket: func(ctx context.Context, urlStr string, tlsConfig *tls.Config, ipAddr string) (jsoncodec.JSONConn, error) { apiConn := testRootAPI{ serverAddrs: params.FromProviderHostsPorts([]network.ProviderHostPorts{{ - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("example3"), NetPort: 3333}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("example5"), NetPort: 5555}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("example3").AsProviderAddress(), NetPort: 3333}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("example5").AsProviderAddress(), NetPort: 5555}, }}), } c.Logf("Dial: %q requested", ipAddr) @@ -410,15 +410,15 @@ func (s *NewAPIClientSuite) TestEndpointFiltering(c *gc.C) { c.Assert(err, jc.ErrorIsNil) serverAddrs := params.FromProviderHostsPorts([]network.ProviderHostPorts{{ - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("0.1.2.3"), NetPort: 1234}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("2001:db8::1"), NetPort: 1234}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("10.0.0.1"), NetPort: 1234}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("127.0.0.1"), NetPort: 1234}, - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("169.254.1.1"), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("0.1.2.3").AsProviderAddress(), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("2001:db8::1").AsProviderAddress(), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("10.0.0.1").AsProviderAddress(), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("169.254.1.1").AsProviderAddress(), NetPort: 1234}, //Duplicate - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("0.1.2.3"), NetPort: 1234}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("0.1.2.3").AsProviderAddress(), NetPort: 1234}, //Duplicate host, same IP. - network.ProviderHostPort{ProviderAddress: network.NewProviderAddress("0.1.2.3"), NetPort: 1235}, + network.ProviderHostPort{ProviderAddress: network.NewMachineAddress("0.1.2.3").AsProviderAddress(), NetPort: 1235}, }}) conn, err := juju.NewAPIConnection(juju.NewAPIConnectionParams{ diff --git a/make_functions.sh b/make_functions.sh index d336490b9b9..627d92354f6 100755 --- a/make_functions.sh +++ b/make_functions.sh @@ -70,8 +70,14 @@ build_operator_image() { } wait_for_dpkg() { + # Just in case, wait for cloud-init. + cloud-init status --wait 2> /dev/null || true while sudo lsof /var/lib/dpkg/lock-frontend 2> /dev/null; do echo "Waiting for dpkg lock..." sleep 10 done + while sudo lsof /var/lib/apt/lists/lock 2> /dev/null; do + echo "Waiting for apt lock..." + sleep 10 + done } diff --git a/mongo/mongo.go b/mongo/mongo.go index c55720881cc..7b9b2cce44a 100644 --- a/mongo/mongo.go +++ b/mongo/mongo.go @@ -342,7 +342,7 @@ func checkInstalled(svc MongoSnapService) error { return nil } // Do not attempt to svc.Ensure(). It is not implemented - // for snap. Nor is the config between juju-db 4.4.x is + // for snap. Nor is the config between juju-db > 4.4.x // compatible with juju-db 4.0.x, e.g. tlsCertificateKeyFile // etc. running, err := svc.Running() diff --git a/mongo/mongo_test.go b/mongo/mongo_test.go index 4ae0e2dab89..3819c33eca0 100644 --- a/mongo/mongo_test.go +++ b/mongo/mongo_test.go @@ -70,7 +70,7 @@ func makeEnsureServerParams(dataDir, configDir string) mongo.EnsureServerParams func (s *MongoSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) - testing.PatchExecutable(c, s, "juju-db.mongod", "#!/bin/bash\n\nprintf %s 'db version v4.4.4'\n") + testing.PatchExecutable(c, s, "juju-db.mongod", "#!/bin/bash\n\nprintf %s 'db version v4.4.11'\n") jujuMongodPath, err := exec.LookPath("juju-db.mongod") c.Assert(err, jc.ErrorIsNil) s.PatchValue(&mongo.JujuDbSnapMongodPath, jujuMongodPath) @@ -286,9 +286,9 @@ func (s *MongoSuite) TestNoMongoDir(c *gc.C) { func (s *MongoSuite) TestSelectPeerAddress(c *gc.C) { addresses := network.ProviderAddresses{ - network.NewProviderAddress("126.0.0.1", network.WithScope(network.ScopeMachineLocal)), - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), - network.NewProviderAddress("8.8.8.8", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("126.0.0.1", network.WithScope(network.ScopeMachineLocal)).AsProviderAddress(), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), + network.NewMachineAddress("8.8.8.8", network.WithScope(network.ScopePublic)).AsProviderAddress(), } address := mongo.SelectPeerAddress(addresses) diff --git a/network/network_test.go b/network/network_test.go index 611bfb6448b..2495f2c6082 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -151,7 +151,7 @@ LXC_BRIDGE="ignored"`[1:]) }) s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig) - inputAddresses := corenetwork.NewProviderAddresses( + inputAddresses := corenetwork.NewMachineAddresses([]string{ "127.0.0.1", "2001:db8::1", "10.0.0.1", @@ -165,15 +165,15 @@ LXC_BRIDGE="ignored"`[1:]) "192.168.122.1", // filtered (from virbr0 bridge, 192.168.122.1) "192.168.123.42", "localhost", // unfiltered because it isn't an IP address - ) - filteredAddresses := corenetwork.NewProviderAddresses( + }).AsProviderAddresses() + filteredAddresses := corenetwork.NewMachineAddresses([]string{ "127.0.0.1", "2001:db8::1", "10.0.0.1", "10.0.6.10", "192.168.123.42", "localhost", - ) + }).AsProviderAddresses() c.Assert(network.FilterBridgeAddresses(inputAddresses), jc.DeepEquals, filteredAddresses) } diff --git a/provider/azure/instance.go b/provider/azure/instance.go index 670be42dd1d..d7d9fbe553b 100644 --- a/provider/azure/instance.go +++ b/provider/azure/instance.go @@ -163,20 +163,20 @@ func (inst *azureInstance) Addresses(ctx context.ProviderCallContext) (corenetwo if privateIpAddress == nil { continue } - addresses = append(addresses, corenetwork.NewProviderAddress( + addresses = append(addresses, corenetwork.NewMachineAddress( to.String(privateIpAddress), corenetwork.WithScope(corenetwork.ScopeCloudLocal), - )) + ).AsProviderAddress()) } } for _, pip := range inst.publicIPAddresses { if pip.IPAddress == nil { continue } - addresses = append(addresses, corenetwork.NewProviderAddress( + addresses = append(addresses, corenetwork.NewMachineAddress( to.String(pip.IPAddress), corenetwork.WithScope(corenetwork.ScopePublic), - )) + ).AsProviderAddress()) } return addresses, nil } diff --git a/provider/azure/instance_test.go b/provider/azure/instance_test.go index 44b72e49116..59af003bd3f 100644 --- a/provider/azure/instance_test.go +++ b/provider/azure/instance_test.go @@ -268,9 +268,9 @@ func (s *instanceSuite) TestInstanceAddresses(c *gc.C) { } addresses, err := s.getInstance(c, "machine-0").Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) - c.Assert(addresses, jc.DeepEquals, corenetwork.NewProviderAddresses( + c.Assert(addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{ "10.0.0.4", "10.0.0.5", "1.2.3.4", "1.2.3.5", - )) + }).AsProviderAddresses()) } func (s *instanceSuite) TestMultipleInstanceAddresses(c *gc.C) { @@ -289,15 +289,15 @@ func (s *instanceSuite) TestMultipleInstanceAddresses(c *gc.C) { inst0Addresses, err := instances[0].Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) - c.Assert(inst0Addresses, jc.DeepEquals, corenetwork.NewProviderAddresses( + c.Assert(inst0Addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{ "10.0.0.4", "1.2.3.4", - )) + }).AsProviderAddresses()) inst1Addresses, err := instances[1].Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) - c.Assert(inst1Addresses, jc.DeepEquals, corenetwork.NewProviderAddresses( + c.Assert(inst1Addresses, jc.DeepEquals, corenetwork.NewMachineAddresses([]string{ "10.0.0.5", "1.2.3.5", - )) + }).AsProviderAddresses()) } func (s *instanceSuite) TestIngressRulesEmpty(c *gc.C) { diff --git a/provider/common/bootstrap_test.go b/provider/common/bootstrap_test.go index f55617fa290..7511178adf3 100644 --- a/provider/common/bootstrap_test.go +++ b/provider/common/bootstrap_test.go @@ -576,7 +576,7 @@ func (s *BootstrapSuite) TestSuccess(c *gc.C) { var innerInstanceConfig *instancecfg.InstanceConfig inst := &mockInstance{ id: checkInstanceId, - addresses: network.NewProviderAddresses("testing.invalid"), + addresses: network.NewMachineAddresses([]string{"testing.invalid"}).AsProviderAddresses(), } startInstance := func(ctx envcontext.ProviderCallContext, args environs.StartInstanceParams) ( instances.Instance, @@ -683,7 +683,7 @@ func (s *BootstrapSuite) TestBootstrapFinalizeCloudInitUserData(c *gc.C) { var innerInstanceConfig *instancecfg.InstanceConfig inst := &mockInstance{ id: "i-success", - addresses: network.NewProviderAddresses("testing.invalid"), + addresses: network.NewMachineAddresses([]string{"testing.invalid"}).AsProviderAddresses(), } startInstance := func(ctx envcontext.ProviderCallContext, args environs.StartInstanceParams) ( instances.Instance, @@ -839,7 +839,7 @@ type neverOpensPort struct { } func (n *neverOpensPort) Addresses(ctx envcontext.ProviderCallContext) (network.ProviderAddresses, error) { - return network.NewProviderAddresses(n.addr), nil + return network.NewMachineAddresses([]string{n.addr}).AsProviderAddresses(), nil } func (s *BootstrapSuite) TestWaitSSHTimesOutWaitingForDial(c *gc.C) { @@ -873,7 +873,7 @@ func (c *cancelOnDial) Addresses(ctx envcontext.ProviderCallContext) (network.Pr c.cancel = nil } } - return network.NewProviderAddresses(c.name), nil + return network.NewMachineAddresses([]string{c.name}).AsProviderAddresses(), nil } func (s *BootstrapSuite) TestWaitSSHKilledWaitingForDial(c *gc.C) { @@ -908,7 +908,7 @@ func (ac *addressesChange) Status(ctx envcontext.ProviderCallContext) instance.S } func (ac *addressesChange) Addresses(ctx envcontext.ProviderCallContext) (network.ProviderAddresses, error) { - return network.NewProviderAddresses(ac.addrs[0]...), nil + return network.NewMachineAddresses(ac.addrs[0]).AsProviderAddresses(), nil } func (s *BootstrapSuite) TestWaitSSHRefreshAddresses(c *gc.C) { diff --git a/provider/dummy/environs.go b/provider/dummy/environs.go index 4f7d6d41c88..a480c0fecb8 100644 --- a/provider/dummy/environs.go +++ b/provider/dummy/environs.go @@ -838,7 +838,7 @@ func (e *environ) Bootstrap(ctx environs.BootstrapContext, callCtx context.Provi series := config.PreferredSeries(e.Config()) i := &dummyInstance{ id: BootstrapInstanceId, - addresses: network.NewProviderAddresses("localhost"), + addresses: network.NewMachineAddresses([]string{"localhost"}).AsProviderAddresses(), machineId: agent.BootstrapControllerId, series: series, firewallMode: e.Config().FirewallMode(), @@ -1213,7 +1213,7 @@ func (e *environ) StartInstance(ctx context.ProviderCallContext, args environs.S idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) // Add the addresses we want to see in the machine doc. This means both // IPv4 and IPv6 loopback, as well as the DNS name. - addrs := network.NewProviderAddresses(idString+".dns", "127.0.0.1", "::1") + addrs := network.NewMachineAddresses([]string{idString + ".dns", "127.0.0.1", "::1"}).AsProviderAddresses() logger.Debugf("StartInstance addresses: %v", addrs) i := &dummyInstance{ id: instance.Id(idString), @@ -1477,16 +1477,16 @@ func (env *environ) NetworkInterfaces(ctx context.ProviderCallContext, ids []ins Disabled: i == 2, NoAutoStart: i%2 != 0, Addresses: network.ProviderAddresses{ - network.NewProviderAddress( + network.NewMachineAddress( fmt.Sprintf("0.%d.0.%d", (i+1)*10+idIndex, estate.maxAddr+2), network.WithCIDR(fmt.Sprintf("0.%d.0.0/24", (i+1)*10)), network.WithConfigType(network.ConfigDHCP), - ), + ).AsProviderAddress(), }, - DNSServers: network.NewProviderAddresses("ns1.dummy", "ns2.dummy"), - GatewayAddress: network.NewProviderAddress( + DNSServers: network.NewMachineAddresses([]string{"ns1.dummy", "ns2.dummy"}).AsProviderAddresses(), + GatewayAddress: network.NewMachineAddress( fmt.Sprintf("0.%d.0.1", (i+1)*10+idIndex), - ), + ).AsProviderAddress(), Origin: network.OriginProvider, } } diff --git a/provider/dummy/environs_test.go b/provider/dummy/environs_test.go index 33195ab0c60..e2ab322e784 100644 --- a/provider/dummy/environs_test.go +++ b/provider/dummy/environs_test.go @@ -249,11 +249,11 @@ func (s *suite) TestNetworkInterfaces(c *gc.C) { MACAddress: "aa:bb:cc:dd:ee:f0", Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "0.10.0.2", corenetwork.WithCIDR("0.10.0.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, - DNSServers: corenetwork.NewProviderAddresses("ns1.dummy", "ns2.dummy"), - GatewayAddress: corenetwork.NewProviderAddress("0.10.0.1"), + ).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.dummy", "ns2.dummy"}).AsProviderAddresses(), + GatewayAddress: corenetwork.NewMachineAddress("0.10.0.1").AsProviderAddress(), Origin: corenetwork.OriginProvider, }, { ProviderId: "dummy-eth1", @@ -265,11 +265,11 @@ func (s *suite) TestNetworkInterfaces(c *gc.C) { MACAddress: "aa:bb:cc:dd:ee:f1", Disabled: false, NoAutoStart: true, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "0.20.0.2", corenetwork.WithCIDR("0.20.0.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, - DNSServers: corenetwork.NewProviderAddresses("ns1.dummy", "ns2.dummy"), - GatewayAddress: corenetwork.NewProviderAddress("0.20.0.1"), + ).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.dummy", "ns2.dummy"}).AsProviderAddresses(), + GatewayAddress: corenetwork.NewMachineAddress("0.20.0.1").AsProviderAddress(), Origin: corenetwork.OriginProvider, }, { ProviderId: "dummy-eth2", @@ -281,11 +281,11 @@ func (s *suite) TestNetworkInterfaces(c *gc.C) { MACAddress: "aa:bb:cc:dd:ee:f2", Disabled: true, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "0.30.0.2", corenetwork.WithCIDR("0.30.0.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, - DNSServers: corenetwork.NewProviderAddresses("ns1.dummy", "ns2.dummy"), - GatewayAddress: corenetwork.NewProviderAddress("0.30.0.1"), + ).AsProviderAddress()}, + DNSServers: corenetwork.NewMachineAddresses([]string{"ns1.dummy", "ns2.dummy"}).AsProviderAddresses(), + GatewayAddress: corenetwork.NewMachineAddress("0.30.0.1").AsProviderAddress(), Origin: corenetwork.OriginProvider, }} infoList, err := e.NetworkInterfaces(s.callCtx, []instance.Id{"i-42"}) diff --git a/provider/ec2/environ.go b/provider/ec2/environ.go index 1bb882e5218..8ad78331a6b 100644 --- a/provider/ec2/environ.go +++ b/provider/ec2/environ.go @@ -1511,22 +1511,22 @@ func mapNetworkInterface(iface types.NetworkInterface, subnet types.Subnet) netw // "PrivateIPAddress" field. The code below arranges so that // the primary IP is always added first with any additional // private IPs appended after it. - Addresses: network.ProviderAddresses{network.NewProviderAddress( + Addresses: network.ProviderAddresses{network.NewMachineAddress( privateAddress, network.WithScope(network.ScopeCloudLocal), network.WithCIDR(subnetCIDR), network.WithConfigType(network.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: network.OriginProvider, } for _, privAddr := range iface.PrivateIpAddresses { if ip := aws.ToString(privAddr.Association.PublicIp); ip != "" { - ni.ShadowAddresses = append(ni.ShadowAddresses, network.NewProviderAddress( + ni.ShadowAddresses = append(ni.ShadowAddresses, network.NewMachineAddress( ip, network.WithScope(network.ScopePublic), network.WithConfigType(network.ConfigDHCP), - )) + ).AsProviderAddress()) } if aws.ToString(privAddr.PrivateIpAddress) == privateAddress { @@ -1535,12 +1535,12 @@ func mapNetworkInterface(iface types.NetworkInterface, subnet types.Subnet) netw // An EC2 interface is connected to a single subnet, // so we assume other addresses are in the same subnet. - ni.Addresses = append(ni.Addresses, network.NewProviderAddress( + ni.Addresses = append(ni.Addresses, network.NewMachineAddress( privateAddress, network.WithScope(network.ScopeCloudLocal), network.WithCIDR(subnetCIDR), network.WithConfigType(network.ConfigDHCP), - )) + ).AsProviderAddress()) } return ni diff --git a/provider/ec2/local_test.go b/provider/ec2/local_test.go index 904e8a47507..be94343fb44 100644 --- a/provider/ec2/local_test.go +++ b/provider/ec2/local_test.go @@ -1406,8 +1406,8 @@ func (t *localServerSuite) TestAddresses(c *gc.C) { // Expected values use Address type but really contain a regexp for // the value rather than a valid ip or hostname. expected := corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("8.0.0.*", corenetwork.WithScope(corenetwork.ScopePublic)), - corenetwork.NewProviderAddress("127.0.0.*", corenetwork.WithScope(corenetwork.ScopeCloudLocal)), + corenetwork.NewMachineAddress("8.0.0.*", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), + corenetwork.NewMachineAddress("127.0.0.*", corenetwork.WithScope(corenetwork.ScopeCloudLocal)).AsProviderAddress(), } expected[0].Type = corenetwork.IPv4Address expected[1].Type = corenetwork.IPv4Address @@ -1755,19 +1755,19 @@ func (t *localServerSuite) assertInterfaceLooksValid(c *gc.C, expIfaceID, expDev Disabled: false, NoAutoStart: false, InterfaceType: corenetwork.EthernetDevice, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( addr, corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR(cidr), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, // Each machine is also assigned a shadow IP with the pattern: // 73.37.0.X where X=(provider iface ID + 1) - ShadowAddresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + ShadowAddresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( fmt.Sprintf("73.37.0.%d", expIfaceID+1), corenetwork.WithScope(corenetwork.ScopePublic), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, AvailabilityZones: zones, Origin: corenetwork.OriginProvider, } diff --git a/provider/equinix/environ.go b/provider/equinix/environ.go index b6a7d4e6da0..9f730030797 100644 --- a/provider/equinix/environ.go +++ b/provider/equinix/environ.go @@ -311,6 +311,16 @@ func (e *environ) StartInstance(ctx context.ProviderCallContext, args environs.S ) } + // NOTE(achilleasa): ensure that /etc/hosts entry for the loopback dev + // references the juju-assigned hostname before localhost. Otherwise, + // running 'hostname -f' would return localhost whereas 'hostname' + // returns the juju-assigned host (see LP1956538). + if _, err := series.UbuntuSeriesVersion(args.InstanceConfig.Series); err == nil { + cloudCfg.AddScripts( + `sed -i -e "/127\.0\.0\.1/c\127\.0\.0\.1 $(hostname) localhost" /etc/hosts`, + ) + } + // NOTE(achilleasa): The following script applies a set of equinix-specific // networking fixes: // diff --git a/provider/gce/environ_network.go b/provider/gce/environ_network.go index 987f8373969..b62c1f2ba4f 100644 --- a/provider/gce/environ_network.go +++ b/provider/gce/environ_network.go @@ -200,7 +200,7 @@ func (e *environ) NetworkInterfaces(ctx context.ProviderCallContext, ids []insta } shadowAddrs = append(shadowAddrs, - corenetwork.NewProviderAddress(accessConf.NatIP, corenetwork.WithScope(corenetwork.ScopePublic)), + corenetwork.NewMachineAddress(accessConf.NatIP, corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), ) } @@ -213,12 +213,12 @@ func (e *environ) NetworkInterfaces(ctx context.ProviderCallContext, ids []insta ProviderNetworkId: details.network, AvailabilityZones: copyStrings(zones), InterfaceName: iface.Name, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( iface.NetworkIP, corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR(details.cidr), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, ShadowAddresses: shadowAddrs, InterfaceType: corenetwork.EthernetDevice, Disabled: false, diff --git a/provider/gce/environ_network_test.go b/provider/gce/environ_network_test.go index c51f229780e..f8e09088ccf 100644 --- a/provider/gce/environ_network_test.go +++ b/provider/gce/environ_network_test.go @@ -212,12 +212,12 @@ func (s *environNetSuite) TestInterfaces(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }}) } @@ -299,12 +299,12 @@ func (s *environNetSuite) TestInterfacesForMultipleInstances(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }}) @@ -320,14 +320,14 @@ func (s *environNetSuite) TestInterfacesForMultipleInstances(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.42", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, ShadowAddresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)), + corenetwork.NewMachineAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }, Origin: corenetwork.OriginProvider, }, { @@ -340,12 +340,12 @@ func (s *environNetSuite) TestInterfacesForMultipleInstances(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.20.42", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.20.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }}) } @@ -371,12 +371,12 @@ func (s *environNetSuite) TestPartialInterfacesForMultipleInstances(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }}) @@ -418,12 +418,12 @@ func (s *environNetSuite) TestInterfacesMulti(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }, { DeviceIndex: 1, @@ -435,14 +435,14 @@ func (s *environNetSuite) TestInterfacesMulti(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.20.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.20.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, ShadowAddresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)), + corenetwork.NewMachineAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }, Origin: corenetwork.OriginProvider, }}) @@ -481,14 +481,14 @@ func (s *environNetSuite) TestInterfacesLegacy(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.240.0.2", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.240.0.0/16"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, ShadowAddresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)), + corenetwork.NewMachineAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }, Origin: corenetwork.OriginProvider, }}) @@ -528,12 +528,12 @@ func (s *environNetSuite) TestInterfacesSameSubnetwork(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.3", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, Origin: corenetwork.OriginProvider, }, { DeviceIndex: 1, @@ -545,14 +545,14 @@ func (s *environNetSuite) TestInterfacesSameSubnetwork(c *gc.C) { InterfaceType: corenetwork.EthernetDevice, Disabled: false, NoAutoStart: false, - Addresses: corenetwork.ProviderAddresses{corenetwork.NewProviderAddress( + Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress( "10.0.10.4", corenetwork.WithScope(corenetwork.ScopeCloudLocal), corenetwork.WithCIDR("10.0.10.0/24"), corenetwork.WithConfigType(corenetwork.ConfigDHCP), - )}, + ).AsProviderAddress()}, ShadowAddresses: corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)), + corenetwork.NewMachineAddress("25.185.142.227", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }, Origin: corenetwork.OriginProvider, }}) diff --git a/provider/gce/google/network_test.go b/provider/gce/google/network_test.go index 7fc06372595..680d0ef0fa9 100644 --- a/provider/gce/google/network_test.go +++ b/provider/gce/google/network_test.go @@ -90,7 +90,7 @@ func (s *networkSuite) TestExtractAddresses(c *gc.C) { addresses := google.ExtractAddresses(&s.NetworkInterface) c.Check(addresses, jc.DeepEquals, []network.ProviderAddress{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), }) } @@ -100,7 +100,7 @@ func (s *networkSuite) TestExtractAddressesExternal(c *gc.C) { addresses := google.ExtractAddresses(&s.NetworkInterface) c.Check(addresses, jc.DeepEquals, []network.ProviderAddress{ - network.NewProviderAddress("8.8.8.8", network.WithScope(network.ScopePublic)), + network.NewMachineAddress("8.8.8.8", network.WithScope(network.ScopePublic)).AsProviderAddress(), }) } diff --git a/provider/gce/google/testing_test.go b/provider/gce/google/testing_test.go index 56fec74bd0a..06bb3d07100 100644 --- a/provider/gce/google/testing_test.go +++ b/provider/gce/google/testing_test.go @@ -95,7 +95,7 @@ func (s *BaseSuite) SetUpTest(c *gc.C) { }}, } s.Addresses = []network.ProviderAddress{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), } s.RawMetadata = compute.Metadata{ Items: []*compute.MetadataItems{{ diff --git a/provider/gce/testing_test.go b/provider/gce/testing_test.go index 44206990005..a124448bd61 100644 --- a/provider/gce/testing_test.go +++ b/provider/gce/testing_test.go @@ -189,7 +189,7 @@ func (s *BaseSuiteUnpatched) initInst(c *gc.C) { metadataKeyWindowsSysprep: fmt.Sprintf(winSetHostnameScript, "juju.*"), } s.Addresses = []network.ProviderAddress{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), } s.Instance = s.NewInstance(c, "spam") s.BaseInstance = s.Instance.base diff --git a/provider/lxd/environ_network.go b/provider/lxd/environ_network.go index b0520350bdb..db55e7ea49b 100644 --- a/provider/lxd/environ_network.go +++ b/provider/lxd/environ_network.go @@ -91,7 +91,7 @@ func (e *environ) Subnets(ctx context.ProviderCallContext, inst instance.Id, sub } for _, stateAddr := range state.Addresses { - netAddr := network.NewProviderAddress(stateAddr.Address) + netAddr := network.NewMachineAddress(stateAddr.Address).AsProviderAddress() if netAddr.Scope == network.ScopeLinkLocal || netAddr.Scope == network.ScopeMachineLocal { continue } @@ -165,7 +165,7 @@ func (e *environ) subnetDetectionFallback(srv Server, inst instance.Id, keepSubn } for _, guestAddr := range netInfo.Addresses { - netAddr := network.NewProviderAddress(guestAddr.Address) + netAddr := network.NewMachineAddress(guestAddr.Address).AsProviderAddress() if netAddr.Scope == network.ScopeLinkLocal || netAddr.Scope == network.ScopeMachineLocal { continue } @@ -308,7 +308,7 @@ func makeInterfaceInfo(container *lxdapi.Container, guestNetworkName string, net // the primary address and is used to populate the interface CIDR and // subnet ID fields. for _, addr := range netInfo.Addresses { - netAddr := network.NewProviderAddress(addr.Address) + netAddr := network.NewMachineAddress(addr.Address).AsProviderAddress() if netAddr.Scope == network.ScopeLinkLocal || netAddr.Scope == network.ScopeMachineLocal { continue } diff --git a/provider/lxd/environ_network_test.go b/provider/lxd/environ_network_test.go index 5e5ba93e9f9..a7145df90b9 100644 --- a/provider/lxd/environ_network_test.go +++ b/provider/lxd/environ_network_test.go @@ -395,9 +395,9 @@ func (s *environNetSuite) TestNetworkInterfaces(c *gc.C) { ProviderId: "nic-00:16:3e:19:29:cb", ProviderSubnetId: "subnet-lxdbr0-10.55.158.0/24", ProviderNetworkId: "net-lxdbr0", - Addresses: network.ProviderAddresses{network.NewProviderAddress( + Addresses: network.ProviderAddresses{network.NewMachineAddress( "10.55.158.99", network.WithCIDR("10.55.158.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + ).AsProviderAddress()}, }, { DeviceIndex: 1, @@ -410,9 +410,9 @@ func (s *environNetSuite) TestNetworkInterfaces(c *gc.C) { ProviderId: "nic-00:16:3e:fe:fe:fe", ProviderSubnetId: "subnet-ovsbr0-10.42.42.0/24", ProviderNetworkId: "net-ovsbr0", - Addresses: network.ProviderAddresses{network.NewProviderAddress( + Addresses: network.ProviderAddresses{network.NewMachineAddress( "10.42.42.99", network.WithCIDR("10.42.42.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + ).AsProviderAddress()}, }, }, } @@ -471,9 +471,9 @@ func (s *environNetSuite) TestNetworkInterfacesPartialResults(c *gc.C) { ProviderId: "nic-00:16:3e:19:29:cb", ProviderSubnetId: "subnet-lxdbr0-10.55.158.0/24", ProviderNetworkId: "net-lxdbr0", - Addresses: network.ProviderAddresses{network.NewProviderAddress( + Addresses: network.ProviderAddresses{network.NewMachineAddress( "10.55.158.99", network.WithCIDR("10.55.158.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + ).AsProviderAddress()}, }, }, nil, // slot for second instance is nil as the container was not found diff --git a/provider/lxd/testing_test.go b/provider/lxd/testing_test.go index 3327617e53d..a5506f1091f 100644 --- a/provider/lxd/testing_test.go +++ b/provider/lxd/testing_test.go @@ -219,7 +219,7 @@ func (s *BaseSuiteUnpatched) initInst(c *gc.C) { "limits.memory": strconv.Itoa(3750 * 1024 * 1024), } s.Addresses = network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), } // NOTE: the instance ids used throughout this package are not at all @@ -652,7 +652,7 @@ func (conn *StubClient) ContainerAddresses(name string) ([]network.ProviderAddre } return network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), }, nil } diff --git a/provider/maas/devices.go b/provider/maas/devices.go index f25d3a86062..f1f79a39902 100644 --- a/provider/maas/devices.go +++ b/provider/maas/devices.go @@ -233,20 +233,24 @@ func (env *maasEnviron) deviceInterfaceInfo(deviceID instance.Id, nameToParentNa // long-standing last-write-wins behavior that was // present in the original code. Do we need to revisit // this in the future and append link addresses to the list? - nicInfo.Addresses = corenetwork.ProviderAddresses{corenetwork.NewProviderAddressInSpace( - link.Subnet.Space, - link.IPAddress, - corenetwork.WithCIDR(link.Subnet.CIDR), - corenetwork.WithConfigType(configType), - )} + nicInfo.Addresses = corenetwork.ProviderAddresses{ + corenetwork.NewMachineAddress( + link.IPAddress, + corenetwork.WithCIDR(link.Subnet.CIDR), + corenetwork.WithConfigType(configType), + ).AsProviderAddress(corenetwork.WithSpaceName(link.Subnet.Space)), + } nicInfo.ProviderSubnetId = corenetwork.Id(strconv.Itoa(link.Subnet.ID)) nicInfo.ProviderAddressId = corenetwork.Id(strconv.Itoa(link.ID)) if link.Subnet.GatewayIP != "" { - nicInfo.GatewayAddress = corenetwork.NewProviderAddressInSpace(link.Subnet.Space, link.Subnet.GatewayIP) + nicInfo.GatewayAddress = corenetwork.NewMachineAddress( + link.Subnet.GatewayIP, + ).AsProviderAddress(corenetwork.WithSpaceName(link.Subnet.Space)) } if len(link.Subnet.DNSServers) > 0 { - nicInfo.DNSServers = corenetwork.NewProviderAddressesInSpace( - link.Subnet.Space, link.Subnet.DNSServers...) + nicInfo.DNSServers = corenetwork.NewMachineAddresses( + link.Subnet.DNSServers, + ).AsProviderAddresses(corenetwork.WithSpaceName(link.Subnet.Space)) } interfaceInfo = append(interfaceInfo, nicInfo) @@ -320,19 +324,24 @@ func (env *maasEnviron) deviceInterfaceInfo2( // NOTE(achilleasa): the original code used a last-write-wins // policy. Do we need to append link addresses to the list? - nicInfo.Addresses = corenetwork.ProviderAddresses{corenetwork.NewProviderAddressInSpace( - subnet.Space(), - link.IPAddress(), - corenetwork.WithCIDR(subnet.CIDR()), - corenetwork.WithConfigType(configType), - )} + nicInfo.Addresses = corenetwork.ProviderAddresses{ + corenetwork.NewMachineAddress( + link.IPAddress(), + corenetwork.WithCIDR(subnet.CIDR()), + corenetwork.WithConfigType(configType), + ).AsProviderAddress(corenetwork.WithSpaceName(subnet.Space())), + } nicInfo.ProviderSubnetId = corenetwork.Id(strconv.Itoa(subnet.ID())) nicInfo.ProviderAddressId = corenetwork.Id(strconv.Itoa(link.ID())) if subnet.Gateway() != "" { - nicInfo.GatewayAddress = corenetwork.NewProviderAddressInSpace(subnet.Space(), subnet.Gateway()) + nicInfo.GatewayAddress = corenetwork.NewMachineAddress( + subnet.Gateway(), + ).AsProviderAddress(corenetwork.WithSpaceName(subnet.Space())) } if len(subnet.DNSServers()) > 0 { - nicInfo.DNSServers = corenetwork.NewProviderAddressesInSpace(subnet.Space(), subnet.DNSServers()...) + nicInfo.DNSServers = corenetwork.NewMachineAddresses( + subnet.DNSServers(), + ).AsProviderAddresses(corenetwork.WithSpaceName(subnet.Space())) } interfaceInfo = append(interfaceInfo, nicInfo) diff --git a/provider/maas/interfaces.go b/provider/maas/interfaces.go index fd50ce07f5d..ff217392996 100644 --- a/provider/maas/interfaces.go +++ b/provider/maas/interfaces.go @@ -223,7 +223,7 @@ func maasNetworkInterfaces( // NOTE(achilleasa): the original code used a last-write-wins // policy. Do we need to append link addresses to the list? nicInfo.Addresses = corenetwork.ProviderAddresses{ - corenetwork.NewProviderAddress(link.IPAddress(), corenetwork.WithConfigType(configType)), + corenetwork.NewMachineAddress(link.IPAddress(), corenetwork.WithConfigType(configType)).AsProviderAddress(), } nicInfo.ProviderAddressId = corenetwork.Id(fmt.Sprintf("%v", link.ID())) } @@ -244,8 +244,9 @@ func maasNetworkInterfaces( // Now we know the subnet and space, we can update the address to // store the space with it. - nicInfo.Addresses[0] = corenetwork.NewProviderAddressInSpace( - space, link.IPAddress(), corenetwork.WithCIDR(sub.CIDR()), corenetwork.WithConfigType(configType)) + nicInfo.Addresses[0] = corenetwork.NewMachineAddress( + link.IPAddress(), corenetwork.WithCIDR(sub.CIDR()), corenetwork.WithConfigType(configType), + ).AsProviderAddress(corenetwork.WithSpaceName(space)) spaceId, ok := subnetsMap[sub.CIDR()] if !ok { @@ -257,8 +258,8 @@ func maasNetworkInterfaces( nicInfo.ProviderSpaceId = spaceId } - gwAddr := corenetwork.NewProviderAddressInSpace(space, sub.Gateway()) - nicInfo.DNSServers = corenetwork.NewProviderAddressesInSpace(space, sub.DNSServers()...) + gwAddr := corenetwork.NewMachineAddress(sub.Gateway()).AsProviderAddress(corenetwork.WithSpaceName(space)) + nicInfo.DNSServers = corenetwork.NewMachineAddresses(sub.DNSServers()).AsProviderAddresses(corenetwork.WithSpaceName(space)) if ok { gwAddr.ProviderSpaceID = spaceId for i := range nicInfo.DNSServers { diff --git a/provider/maas/interfaces_test.go b/provider/maas/interfaces_test.go index 27533b5313d..ecd65c003dc 100644 --- a/provider/maas/interfaces_test.go +++ b/provider/maas/interfaces_test.go @@ -20,7 +20,7 @@ var _ = gc.Suite(&interfacesSuite{}) func newAddressOnSpaceWithId( space string, id network.Id, address string, options ...func(network.AddressMutator), ) network.ProviderAddress { - newAddress := network.NewProviderAddressInSpace(space, address, options...) + newAddress := network.NewMachineAddress(address, options...).AsProviderAddress(network.WithSpaceName(space)) newAddress.ProviderSpaceID = id return newAddress } @@ -816,13 +816,18 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 0, @@ -837,13 +842,18 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 1, @@ -859,13 +869,15 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "802.1q", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "admin", "10.50.19.103", network.WithCIDR("10.50.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.50.19.103", network.WithCIDR("10.50.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("admin")), + }, DNSServers: nil, DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("admin", "10.50.19.2"), + GatewayAddress: network.NewMachineAddress("10.50.19.2").AsProviderAddress(network.WithSpaceName("admin")), Origin: network.OriginProvider, }, { DeviceIndex: 2, @@ -881,13 +893,15 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "802.1q", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "public", "10.100.19.103", network.WithCIDR("10.100.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.100.19.103", network.WithCIDR("10.100.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("public")), + }, DNSServers: nil, DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("public", "10.100.19.2"), + GatewayAddress: network.NewMachineAddress("10.100.19.2").AsProviderAddress(network.WithSpaceName("public")), Origin: network.OriginProvider, }, { DeviceIndex: 3, @@ -925,13 +939,18 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 4, @@ -946,13 +965,18 @@ func (s *interfacesSuite) TestMAAS2NetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 5, @@ -1030,13 +1054,18 @@ func (s *interfacesSuite) TestMAAS2InterfacesNilVLAN(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }} diff --git a/provider/maas/maas2_environ_whitebox_test.go b/provider/maas/maas2_environ_whitebox_test.go index 3fb283c7208..b062be4fd0f 100644 --- a/provider/maas/maas2_environ_whitebox_test.go +++ b/provider/maas/maas2_environ_whitebox_test.go @@ -940,13 +940,18 @@ func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.103", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 0, @@ -961,13 +966,18 @@ func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) { InterfaceType: "ethernet", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "default", "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("default", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("default")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("default")), DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("default", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("default")), Origin: network.OriginProvider, }, { DeviceIndex: 1, @@ -983,13 +993,15 @@ func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) { InterfaceType: "802.1q", Disabled: false, NoAutoStart: false, - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "admin", "10.50.19.103", network.WithCIDR("10.50.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.50.19.103", network.WithCIDR("10.50.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("admin")), + }, DNSServers: nil, DNSSearchDomains: nil, MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("admin", "10.50.19.2"), + GatewayAddress: network.NewMachineAddress("10.50.19.2").AsProviderAddress(network.WithSpaceName("admin")), Origin: network.OriginProvider, }} c.Assert(result.NetworkInfo, jc.DeepEquals, expected) @@ -1098,7 +1110,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSingleNic(c *gc.C) prepared := network.InterfaceInfos{{ MACAddress: "52:54:00:70:9b:fe", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }} ignored := names.NewMachineTag("1/lxd/0") @@ -1114,12 +1126,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSingleNic(c *gc.C) ProviderAddressId: "480", InterfaceName: "eth1", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "192.168.1.127", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "192.168.1.127", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "192.168.1.1"), + GatewayAddress: network.NewMachineAddress("192.168.1.1").AsProviderAddress(network.WithSpaceName("freckles")), Routes: []network.Route{{ DestinationCIDR: subnet1.CIDR(), GatewayIP: "192.168.1.1", @@ -1228,7 +1245,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesNoStaticRoutesAPI( prepared := network.InterfaceInfos{{ MACAddress: "52:54:00:70:9b:fe", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }} ignored := names.NewMachineTag("1/lxd/0") @@ -1244,12 +1261,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesNoStaticRoutesAPI( ProviderAddressId: "480", InterfaceName: "eth0", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.104", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("freckles")), Routes: []network.Route{}, Origin: network.OriginProvider, }} @@ -1323,7 +1345,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesStaticRoutesDenied prepared := network.InterfaceInfos{{ MACAddress: "52:54:00:70:9b:fe", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }} ignored := names.NewMachineTag("1/lxd/0") @@ -1469,11 +1491,11 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) { prepared := network.InterfaceInfos{{ MACAddress: "53:54:00:70:9b:ff", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }, { MACAddress: "52:54:00:70:9b:f4", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("192.168.1.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("192.168.1.0/24")).AsProviderAddress()}, InterfaceName: "eth1", }} expected := network.InterfaceInfos{{ @@ -1485,12 +1507,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) { ProviderAddressId: "480", InterfaceName: "eth0", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "10.20.19.127", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.127", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("freckles")), Origin: network.OriginProvider, }, { DeviceIndex: 1, @@ -1501,12 +1528,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) { ProviderAddressId: "481", InterfaceName: "eth1", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "192.168.1.127", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "192.168.1.127", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "192.168.1.1"), + GatewayAddress: network.NewMachineAddress("192.168.1.1").AsProviderAddress(network.WithSpaceName("freckles")), Routes: []network.Route{{ DestinationCIDR: "10.20.19.0/24", GatewayIP: "192.168.1.1", @@ -1587,7 +1619,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesMachinesError(c *g env = suite.makeEnviron(c, nil) prepared := network.InterfaceInfos{{ InterfaceName: "eth0", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, }} ignored := names.NewMachineTag("1/lxd/0") _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) @@ -1623,7 +1655,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateDeviceError( env = suite.makeEnviron(c, nil) prepared := network.InterfaceInfos{{ InterfaceName: "eth0", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, MACAddress: "DEADBEEF", }} ignored := names.NewMachineTag("1/lxd/0") @@ -1767,12 +1799,12 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateInterfaceErr prepared := network.InterfaceInfos{ { InterfaceName: "eth0", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, MACAddress: "DEADBEEF", }, { InterfaceName: "eth1", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.20.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.20.0/24")).AsProviderAddress()}, MACAddress: "DEADBEEE", }, } @@ -1825,12 +1857,12 @@ func (suite *maas2EnvironSuite) TestAllocateContainerAddressesLinkSubnetError(c prepared := network.InterfaceInfos{ { InterfaceName: "eth0", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, MACAddress: "DEADBEEF", }, { InterfaceName: "eth1", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.20.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.20.0/24")).AsProviderAddress()}, MACAddress: "DEADBEEE", }, } @@ -1946,7 +1978,7 @@ func (suite *maas2EnvironSuite) TestAllocateContainerReuseExistingDevice(c *gc.C prepared := network.InterfaceInfos{{ MACAddress: "53:54:00:70:9b:ff", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }} containerTag := names.NewMachineTag("1/lxd/0") @@ -1962,12 +1994,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerReuseExistingDevice(c *gc.C ProviderAddressId: "480", InterfaceName: "eth0", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "space-1", "10.20.19.105", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("space-1", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.105", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("space-1")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("space-1")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("space-1", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("space-1")), Routes: []network.Route{}, Origin: network.OriginProvider, }} @@ -2142,11 +2179,11 @@ func (suite *maas2EnvironSuite) TestAllocateContainerRefusesReuseInvalidNIC(c *g prepared := network.InterfaceInfos{{ MACAddress: "53:54:00:70:88:aa", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("10.20.19.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("10.20.19.0/24")).AsProviderAddress()}, InterfaceName: "eth0", }, { MACAddress: "53:54:00:70:88:bb", - Addresses: network.ProviderAddresses{network.NewProviderAddress("", network.WithCIDR("192.168.1.0/24"))}, + Addresses: network.ProviderAddresses{network.NewMachineAddress("", network.WithCIDR("192.168.1.0/24")).AsProviderAddress()}, InterfaceName: "eth1", }} containerTag := names.NewMachineTag("1/lxd/0") @@ -2162,12 +2199,17 @@ func (suite *maas2EnvironSuite) TestAllocateContainerRefusesReuseInvalidNIC(c *g ProviderAddressId: "480", InterfaceName: "eth0", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "10.20.19.105", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "10.20.19.2", "10.20.19.3"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "10.20.19.105", network.WithCIDR("10.20.19.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "10.20.19.2", + "10.20.19.3", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "10.20.19.2"), + GatewayAddress: network.NewMachineAddress("10.20.19.2").AsProviderAddress(network.WithSpaceName("freckles")), Routes: []network.Route{}, Origin: network.OriginProvider, }, { @@ -2180,12 +2222,16 @@ func (suite *maas2EnvironSuite) TestAllocateContainerRefusesReuseInvalidNIC(c *g ProviderAddressId: "481", InterfaceName: "eth1", InterfaceType: "ethernet", - Addresses: network.ProviderAddresses{network.NewProviderAddressInSpace( - "freckles", "192.168.1.101", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), - )}, - DNSServers: network.NewProviderAddressesInSpace("freckles", "192.168.1.2"), + Addresses: network.ProviderAddresses{ + network.NewMachineAddress( + "192.168.1.101", network.WithCIDR("192.168.1.0/24"), network.WithConfigType(network.ConfigStatic), + ).AsProviderAddress(network.WithSpaceName("freckles")), + }, + DNSServers: network.NewMachineAddresses([]string{ + "192.168.1.2", + }).AsProviderAddresses(network.WithSpaceName("freckles")), MTU: 1500, - GatewayAddress: network.NewProviderAddressInSpace("freckles", "192.168.1.1"), + GatewayAddress: network.NewMachineAddress("192.168.1.1").AsProviderAddress(network.WithSpaceName("freckles")), Routes: []network.Route{}, Origin: network.OriginProvider, }} diff --git a/provider/oci/networking.go b/provider/oci/networking.go index 57765dc6cea..409bb203937 100644 --- a/provider/oci/networking.go +++ b/provider/oci/networking.go @@ -1062,11 +1062,11 @@ func (e *Environ) networkInterfacesForInstance(ctx envcontext.ProviderCallContex ProviderId: network.Id(*iface.Vnic.Id), MACAddress: *iface.Vnic.MacAddress, Addresses: network.ProviderAddresses{ - network.NewProviderAddress( + network.NewMachineAddress( *iface.Vnic.PrivateIp, network.WithScope(network.ScopeCloudLocal), network.WithCIDR(*subnet.CidrBlock), - ), + ).AsProviderAddress(), }, InterfaceType: network.EthernetDevice, ProviderSubnetId: network.Id(*iface.Vnic.SubnetId), @@ -1074,10 +1074,10 @@ func (e *Environ) networkInterfacesForInstance(ctx envcontext.ProviderCallContex } if iface.Vnic.PublicIp != nil { nic.ShadowAddresses = append(nic.ShadowAddresses, - network.NewProviderAddress( + network.NewMachineAddress( *iface.Vnic.PublicIp, network.WithScope(network.ScopePublic), - ), + ).AsProviderAddress(), ) } info = append(info, nic) diff --git a/provider/oci/networking_test.go b/provider/oci/networking_test.go index 0e6772d5982..4cb8ebeeaf6 100644 --- a/provider/oci/networking_test.go +++ b/provider/oci/networking_test.go @@ -233,10 +233,11 @@ func (s *networkingSuite) TestNetworkInterfaces(c *gc.C) { c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Addresses, gc.DeepEquals, network.ProviderAddresses{ - network.NewProviderAddress( - "1.1.1.1", network.WithScope(network.ScopeCloudLocal), network.WithCIDR("1.0.0.0/8"))}) + network.NewMachineAddress( + "1.1.1.1", network.WithScope(network.ScopeCloudLocal), network.WithCIDR("1.0.0.0/8"), + ).AsProviderAddress()}) c.Assert(info[0].ShadowAddresses, gc.DeepEquals, network.ProviderAddresses{ - network.NewProviderAddress("2.2.2.2", network.WithScope(network.ScopePublic))}) + network.NewMachineAddress("2.2.2.2", network.WithScope(network.ScopePublic)).AsProviderAddress()}) c.Assert(info[0].DeviceIndex, gc.Equals, 0) c.Assert(info[0].ProviderId, gc.Equals, network.Id(vnicID)) c.Assert(info[0].MACAddress, gc.Equals, "aa:aa:aa:aa:aa:aa") diff --git a/provider/openstack/local_test.go b/provider/openstack/local_test.go index 10d7edd46d9..dd5607c891c 100644 --- a/provider/openstack/local_test.go +++ b/provider/openstack/local_test.go @@ -422,11 +422,11 @@ func (s *localServerSuite) assertAddressesWithPublicIP(c *gc.C, cons constraints addr, err := inst.Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) c.Assert(addr, jc.SameContents, network.ProviderAddresses{ - network.NewProviderAddress("10.0.0.1", corenetwork.WithScope(corenetwork.ScopePublic)), - network.NewProviderAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)), - network.NewProviderAddress("::face::000f"), - network.NewProviderAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)), - network.NewProviderAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)), + network.NewMachineAddress("10.0.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)).AsProviderAddress(), + network.NewMachineAddress("::face::000f").AsProviderAddress(), + network.NewMachineAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }) bootstrapFinished = true return nil @@ -463,10 +463,10 @@ func (s *localServerSuite) assertAddressesWithoutPublicIP(c *gc.C, cons constrai addr, err := inst.Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) c.Assert(addr, jc.SameContents, network.ProviderAddresses{ - network.NewProviderAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)), - network.NewProviderAddress("::face::000f"), - network.NewProviderAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)), - network.NewProviderAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)), + network.NewMachineAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)).AsProviderAddress(), + network.NewMachineAddress("::face::000f").AsProviderAddress(), + network.NewMachineAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), + network.NewMachineAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(), }) bootstrapFinished = true return nil diff --git a/provider/openstack/provider.go b/provider/openstack/provider.go index 4dc9bb4f361..bee05c0fad2 100644 --- a/provider/openstack/provider.go +++ b/provider/openstack/provider.go @@ -506,7 +506,7 @@ func (inst *openstackInstance) Addresses(ctx context.ProviderCallContext) (netwo func convertNovaAddresses(publicIP string, addresses map[string][]nova.IPAddress) network.ProviderAddresses { var machineAddresses []network.ProviderAddress if publicIP != "" { - publicAddr := network.NewProviderAddress(publicIP, network.WithScope(network.ScopePublic)) + publicAddr := network.NewMachineAddress(publicIP, network.WithScope(network.ScopePublic)).AsProviderAddress() machineAddresses = append(machineAddresses, publicAddr) } // TODO(gz) Network ordering may be significant but is not preserved by @@ -527,7 +527,7 @@ func convertNovaAddresses(publicIP string, addresses map[string][]nova.IPAddress if address.Version == 6 { addrType = network.IPv6Address } - machineAddr := network.NewProviderAddress(address.Address, network.WithScope(networkScope)) + machineAddr := network.NewMachineAddress(address.Address, network.WithScope(networkScope)).AsProviderAddress() if machineAddr.Type != addrType { logger.Warningf("derived address type %v, nova reports %v", machineAddr.Type, addrType) } @@ -1492,7 +1492,7 @@ func (e *Environ) networksForInstance( netInfo[i] = network.InterfaceInfo{ InterfaceName: fmt.Sprintf("eth%d", i), MACAddress: port.MACAddress, - Addresses: network.NewProviderAddresses(ips...), + Addresses: network.NewMachineAddresses(ips).AsProviderAddresses(), ConfigType: network.ConfigDHCP, Origin: network.OriginProvider, } diff --git a/provider/openstack/provider_test.go b/provider/openstack/provider_test.go index 3b5ede0df8c..93dc2d9dc60 100644 --- a/provider/openstack/provider_test.go +++ b/provider/openstack/provider_test.go @@ -912,7 +912,7 @@ func (s *providerUnitTests) TestNetworksForInstanceWithAZ(c *gc.C) { netCfg.EXPECT().AddNetworkConfig(network.InterfaceInfos{{ InterfaceName: "eth0", MACAddress: "mac-address", - Addresses: network.NewProviderAddresses("10.10.10.1"), + Addresses: network.NewMachineAddresses([]string{"10.10.10.1"}).AsProviderAddresses(), ConfigType: network.ConfigDHCP, Origin: network.OriginProvider, }}).Return(nil) diff --git a/provider/vsphere/instance.go b/provider/vsphere/instance.go index 4c421f74069..fdc76628e85 100644 --- a/provider/vsphere/instance.go +++ b/provider/vsphere/instance.go @@ -57,7 +57,7 @@ func (inst *environInstance) Addresses(ctx context.ProviderCallContext) (corenet res := make([]corenetwork.ProviderAddress, 0, len(inst.base.Guest.Net)) for _, net := range inst.base.Guest.Net { for _, ip := range net.IpAddress { - res = append(res, corenetwork.NewProviderAddress(ip)) + res = append(res, corenetwork.NewMachineAddress(ip).AsProviderAddress()) } } return res, nil diff --git a/provider/vsphere/instance_test.go b/provider/vsphere/instance_test.go index fe73b4c143b..4072e05f823 100644 --- a/provider/vsphere/instance_test.go +++ b/provider/vsphere/instance_test.go @@ -86,7 +86,7 @@ func (s *InstanceSuite) TestInstanceAddresses(c *gc.C) { addrs, err := instances[0].Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) - c.Assert(addrs, jc.DeepEquals, network.NewProviderAddresses("10.1.1.1", "10.1.1.2", "10.1.1.3")) + c.Assert(addrs, jc.DeepEquals, network.NewMachineAddresses([]string{"10.1.1.1", "10.1.1.2", "10.1.1.3"}).AsProviderAddresses()) addrs, err = instances[1].Addresses(s.callCtx) c.Assert(err, jc.ErrorIsNil) diff --git a/snap/local/wrappers/fetch-oci b/snap/local/wrappers/fetch-oci index 02852b44ce1..91b740e8153 100755 --- a/snap/local/wrappers/fetch-oci +++ b/snap/local/wrappers/fetch-oci @@ -32,7 +32,7 @@ echo "Wait for microk8s to be ready if needed." microk8s.status --wait-ready --timeout 30 2>&1 juju_version=\$(/snap/bin/juju version | rev | cut -d- -f3- | rev) oci_image="docker.io/jujusolutions/jujud-operator:\$juju_version" -mongo_image="docker.io/jujusolutions/juju-db:4.4" +mongo_image="docker.io/jujusolutions/juju-db:5.0" echo "Going to cache images: \$oci_image and \$mongo_image." echo "Pulling: \$oci_image." diff --git a/state/linklayerdevices.go b/state/linklayerdevices.go index 0d83cd00dc4..e6458c095c9 100644 --- a/state/linklayerdevices.go +++ b/state/linklayerdevices.go @@ -611,10 +611,10 @@ func (dev *LinkLayerDevice) EthernetDeviceForBridge( newDev.VLANTag = sub.VLANTag() newDev.IsDefaultGateway = addr.IsDefaultGateway() newDev.Addresses = network.ProviderAddresses{ - network.NewProviderAddress("", network.WithCIDR(sub.CIDR()))} + network.NewMachineAddress("", network.WithCIDR(sub.CIDR())).AsProviderAddress()} } else { newDev.Addresses = network.ProviderAddresses{ - network.NewProviderAddress("", network.WithCIDR(addr.SubnetCIDR()))} + network.NewMachineAddress("", network.WithCIDR(addr.SubnetCIDR())).AsProviderAddress()} } } diff --git a/state/logs.go b/state/logs.go index 19494324f29..0219c9a2d5c 100644 --- a/state/logs.go +++ b/state/logs.go @@ -973,15 +973,9 @@ func collStats(coll *mgo.Collection) (bson.M, error) { {"scale", humanize.KiByte}, }, &result) if err != nil { - // TODO(wallyworld) - this is not needed for mongo 4.4 - if strings.Contains(err.Error(), "not found") { - return nil, errors.Wrap( - err, - errors.NotFoundf("Collection [%s.%s]", coll.Database.Name, coll.Name)) - } return nil, errors.Trace(err) } - // For mongo 4.4, if the collection exists, + // For mongo > 4.4, if the collection exists, // there will be a "capped" attribute. _, ok := result["capped"] if !ok { diff --git a/state/watcher/export_test.go b/state/watcher/export_test.go index 6ff676cced2..9b39b5da714 100644 --- a/state/watcher/export_test.go +++ b/state/watcher/export_test.go @@ -3,9 +3,15 @@ package watcher +import "time" + const ( - TxnWatcherShortWait = txnWatcherShortWait - TxnWatcherErrorShortWait = txnWatcherErrorShortWait + // Add a small fudge factor to the wait times; if we use exactly the same wait time + // for the fake clock, on slow systems the next watcher event can occur before the + // watcher sync can run to process the first event. + + TxnWatcherShortWait = time.Duration(1.1 * float64(txnWatcherShortWait)) + TxnWatcherErrorShortWait = time.Duration(1.1 * float64(txnWatcherErrorShortWait)) ) var OutOfSyncError = outOfSyncError{} diff --git a/state/watcher/logger.go b/state/watcher/logger.go index d1ca6ba8a0a..9e13338a19f 100644 --- a/state/watcher/logger.go +++ b/state/watcher/logger.go @@ -11,6 +11,7 @@ type Logger interface { Infof(format string, values ...interface{}) Debugf(format string, values ...interface{}) Tracef(format string, values ...interface{}) + IsTraceEnabled() bool } type noOpLogger struct{} @@ -20,3 +21,4 @@ func (noOpLogger) Warningf(format string, values ...interface{}) {} func (noOpLogger) Infof(format string, values ...interface{}) {} func (noOpLogger) Debugf(format string, values ...interface{}) {} func (noOpLogger) Tracef(format string, values ...interface{}) {} +func (noOpLogger) IsTraceEnabled() bool { return false } diff --git a/state/watcher/txnwatcher.go b/state/watcher/txnwatcher.go index 0db72dc6593..f2656590764 100644 --- a/state/watcher/txnwatcher.go +++ b/state/watcher/txnwatcher.go @@ -326,11 +326,10 @@ func (w *TxnWatcher) loop() error { logCollection = w.getTxnLogCollection() } added, err := w.sync(logCollection) - if wrench.IsActive("txnwatcher", "sync-error") { + if w.logger.IsTraceEnabled() && wrench.IsActive("txnwatcher", "sync-error") { added = false err = errors.New("test sync watcher error") } - if err == nil { if syncRetryCount > 0 { w.logger.Infof("txn sync watcher recovered after %d retries", syncRetryCount) diff --git a/testcharms/charm-repo/quantal/format2/.dir/ignored b/testcharms/charm-repo/quantal/format2/.dir/ignored deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/testcharms/charm-repo/quantal/format2/.ignored b/testcharms/charm-repo/quantal/format2/.ignored deleted file mode 100644 index 4287ca86179..00000000000 --- a/testcharms/charm-repo/quantal/format2/.ignored +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/testcharms/charm-repo/quantal/format2/metadata.yaml b/testcharms/charm-repo/quantal/format2/metadata.yaml deleted file mode 100644 index 5e4f701d552..00000000000 --- a/testcharms/charm-repo/quantal/format2/metadata.yaml +++ /dev/null @@ -1,6 +0,0 @@ -name: format2 -format: 2 -summary: "Sample charm described in format 2" -description: | - That's a boring charm that is described in - terms of format 2. diff --git a/testcharms/charm-repo/series/format2/hooks/symlink b/testcharms/charm-repo/series/format2/hooks/symlink deleted file mode 120000 index 78bc33729ba..00000000000 --- a/testcharms/charm-repo/series/format2/hooks/symlink +++ /dev/null @@ -1 +0,0 @@ -../target \ No newline at end of file diff --git a/testing/factory/factory.go b/testing/factory/factory.go index 3ac894cbee4..9fe33f6832c 100644 --- a/testing/factory/factory.go +++ b/testing/factory/factory.go @@ -393,7 +393,7 @@ func (factory *Factory) makeMachineReturningPassword(c *gc.C, params *MachinePar // Sensible default values are substituted for missing ones. // Supported charms depend on the charm/testing package. // Currently supported charms: -// all-hooks, category, dummy, format2, logging, monitoring, mysql, +// all-hooks, category, dummy, logging, monitoring, mysql, // mysql-alternative, riak, terracotta, upgrade1, upgrade2, varnish, // varnish-alternative, wordpress. // If params is not specified, defaults are used. diff --git a/worker/apiaddressupdater/apiaddressupdater_test.go b/worker/apiaddressupdater/apiaddressupdater_test.go index ec261c4f4ba..72d8a83ca3d 100644 --- a/worker/apiaddressupdater/apiaddressupdater_test.go +++ b/worker/apiaddressupdater/apiaddressupdater_test.go @@ -83,8 +83,8 @@ func (s *APIAddressUpdaterSuite) TestAddressInitialUpdate(c *gc.C) { defer workertest.CleanKill(c, updater) expServer := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("localhost"), NetPort: 1234}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("127.0.0.1"), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("localhost").AsProviderAddress(), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, }.HostPorts() // SetAPIHostPorts should be called with the initial value. @@ -136,8 +136,8 @@ func (s *APIAddressUpdaterSuite) TestAddressChange(c *gc.C) { c.Fatalf("timed out waiting for SetAPIHostPorts to be called after update") case servers := <-setter.servers: expServer := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("localhost"), NetPort: 1234}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("127.0.0.1"), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("localhost").AsProviderAddress(), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, }.HostPorts() c.Assert(servers, gc.DeepEquals, []corenetwork.HostPorts{expServer}) } @@ -178,8 +178,8 @@ func (s *APIAddressUpdaterSuite) TestAddressChangeEmpty(c *gc.C) { c.Fatalf("timed out waiting for SetAPIHostPorts to be called after update") case servers := <-setter.servers: expServer := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("localhost"), NetPort: 1234}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("127.0.0.1"), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("localhost").AsProviderAddress(), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, }.HostPorts() c.Assert(servers, gc.DeepEquals, []corenetwork.HostPorts{expServer}) } @@ -193,8 +193,8 @@ func (s *APIAddressUpdaterSuite) TestAddressChangeEmpty(c *gc.C) { c.Fatalf("timed out waiting for SetAPIHostPorts to be called after update") case servers := <-setter.servers: expServer := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("localhost"), NetPort: 1234}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("127.0.0.1"), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("localhost").AsProviderAddress(), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, }.HostPorts() c.Assert(servers, gc.DeepEquals, []corenetwork.HostPorts{expServer}) } @@ -274,8 +274,8 @@ LXC_BRIDGE="ignored"`[1:]) } expServer1 := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("localhost"), NetPort: 1234}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("127.0.0.1"), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("localhost").AsProviderAddress(), NetPort: 1234}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("127.0.0.1").AsProviderAddress(), NetPort: 1234}, }.HostPorts() // SetAPIHostPorts should be called with the initial value, and @@ -287,8 +287,8 @@ LXC_BRIDGE="ignored"`[1:]) c.Assert(servers, gc.HasLen, 2) expServerInit := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("10.0.3.3"), NetPort: 4321}, - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("10.0.4.2"), NetPort: 4321}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.3.3").AsProviderAddress(), NetPort: 4321}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.4.2").AsProviderAddress(), NetPort: 4321}, }.HostPorts() c.Assert(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerInit}) } @@ -303,7 +303,7 @@ LXC_BRIDGE="ignored"`[1:]) c.Assert(servers, gc.HasLen, 2) expServerUpd := corenetwork.ProviderHostPorts{ - corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewProviderAddress("10.0.3.3"), NetPort: 4001}, + corenetwork.ProviderHostPort{ProviderAddress: corenetwork.NewMachineAddress("10.0.3.3").AsProviderAddress(), NetPort: 4001}, }.HostPorts() c.Assert(servers, jc.DeepEquals, []corenetwork.HostPorts{expServer1, expServerUpd}) } diff --git a/worker/caasapplicationprovisioner/application.go b/worker/caasapplicationprovisioner/application.go index 83781eed0dc..954011d3637 100644 --- a/worker/caasapplicationprovisioner/application.go +++ b/worker/caasapplicationprovisioner/application.go @@ -83,7 +83,10 @@ func NewAppWorker(config AppWorkerConfig) func() (worker.Worker, error) { } func (a *appWorker) Notify() { - a.changes <- struct{}{} + select { + case a.changes <- struct{}{}: + case <-a.catacomb.Dying(): + } } func (a *appWorker) Kill() { diff --git a/worker/caasapplicationprovisioner/application_test.go b/worker/caasapplicationprovisioner/application_test.go index d889ea65c09..582fef17963 100644 --- a/worker/caasapplicationprovisioner/application_test.go +++ b/worker/caasapplicationprovisioner/application_test.go @@ -283,13 +283,13 @@ func (s *ApplicationWorkerSuite) assertWorker(c *gc.C, name string) { tc.brokerApp.EXPECT().Service().DoAndReturn(func() (*caas.Service, error) { return &caas.Service{ Id: "deadbeef", - Addresses: network.NewProviderAddresses("10.6.6.6"), + Addresses: network.NewMachineAddresses([]string{"10.6.6.6"}).AsProviderAddresses(), }, nil }), tc.unitFacade.EXPECT().UpdateApplicationService(params.UpdateApplicationServiceArg{ ApplicationTag: "application-" + name, ProviderId: "deadbeef", - Addresses: params.FromProviderAddresses(network.NewProviderAddress("10.6.6.6")), + Addresses: params.FromProviderAddresses(network.NewMachineAddress("10.6.6.6").AsProviderAddress()), }).Return(nil), tc.facade.EXPECT().GarbageCollect(name, []names.Tag{names.NewUnitTag("test/0")}, 1, []string{name + "-0"}, false). DoAndReturn( @@ -396,13 +396,13 @@ func (s *ApplicationWorkerSuite) assertWorker(c *gc.C, name string) { tc.brokerApp.EXPECT().Service().DoAndReturn(func() (*caas.Service, error) { return &caas.Service{ Id: "deadbeef", - Addresses: network.NewProviderAddresses("10.6.6.6"), + Addresses: network.NewMachineAddresses([]string{"10.6.6.6"}).AsProviderAddresses(), }, nil }), tc.unitFacade.EXPECT().UpdateApplicationService(params.UpdateApplicationServiceArg{ ApplicationTag: "application-" + name, ProviderId: "deadbeef", - Addresses: params.FromProviderAddresses(network.NewProviderAddress("10.6.6.6")), + Addresses: params.FromProviderAddresses(network.NewMachineAddress("10.6.6.6").AsProviderAddress()), }).Return(nil), tc.facade.EXPECT().GarbageCollect(name, []names.Tag{names.NewUnitTag("test/0")}, 0, []string(nil), false).DoAndReturn(func(appName string, observedUnits []names.Tag, desiredReplicas int, activePodNames []string, force bool) error { return nil @@ -909,7 +909,6 @@ func (s *ApplicationWorkerSuite) TestUpgradeInfoNotFound(c *gc.C) { return nil, errors.NotFoundf("test charm") }), ) - appWorker := s.startAppWorker(c, nil, facade, broker, nil) s.waitDone(c, done) diff --git a/worker/caasapplicationprovisioner/mock_test.go b/worker/caasapplicationprovisioner/mock_test.go index 3262b83d7e2..3887ac7729b 100644 --- a/worker/caasapplicationprovisioner/mock_test.go +++ b/worker/caasapplicationprovisioner/mock_test.go @@ -6,26 +6,12 @@ package caasapplicationprovisioner_test import ( jujutesting "github.com/juju/testing" "github.com/juju/worker/v3" - - "github.com/juju/juju/core/watcher" - "github.com/juju/juju/core/watcher/watchertest" - "github.com/juju/juju/worker/caasapplicationprovisioner" ) //go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/broker_mock.go github.com/juju/juju/worker/caasapplicationprovisioner CAASBroker //go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/facade_mock.go github.com/juju/juju/worker/caasapplicationprovisioner CAASProvisionerFacade //go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/unitfacade_mock.go github.com/juju/juju/worker/caasapplicationprovisioner CAASUnitProvisionerFacade - -type mockFacade struct { - caasapplicationprovisioner.CAASProvisionerFacade - jujutesting.Stub - appWatcher *watchertest.MockStringsWatcher -} - -func (f *mockFacade) WatchApplications() (watcher.StringsWatcher, error) { - f.MethodCall(f, "WatchApplications") - return f.appWatcher, f.NextErr() -} +//go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/runner_mock.go github.com/juju/juju/worker/caasapplicationprovisioner Runner type mockNotifyWorker struct { worker.Worker diff --git a/worker/caasapplicationprovisioner/mocks/runner_mock.go b/worker/caasapplicationprovisioner/mocks/runner_mock.go new file mode 100644 index 00000000000..0a2ef0feead --- /dev/null +++ b/worker/caasapplicationprovisioner/mocks/runner_mock.go @@ -0,0 +1,104 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/juju/juju/worker/caasapplicationprovisioner (interfaces: Runner) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + worker "github.com/juju/worker/v3" +) + +// MockRunner is a mock of Runner interface. +type MockRunner struct { + ctrl *gomock.Controller + recorder *MockRunnerMockRecorder +} + +// MockRunnerMockRecorder is the mock recorder for MockRunner. +type MockRunnerMockRecorder struct { + mock *MockRunner +} + +// NewMockRunner creates a new mock instance. +func NewMockRunner(ctrl *gomock.Controller) *MockRunner { + mock := &MockRunner{ctrl: ctrl} + mock.recorder = &MockRunnerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRunner) EXPECT() *MockRunnerMockRecorder { + return m.recorder +} + +// Kill mocks base method. +func (m *MockRunner) Kill() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Kill") +} + +// Kill indicates an expected call of Kill. +func (mr *MockRunnerMockRecorder) Kill() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Kill", reflect.TypeOf((*MockRunner)(nil).Kill)) +} + +// StartWorker mocks base method. +func (m *MockRunner) StartWorker(arg0 string, arg1 func() (worker.Worker, error)) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StartWorker", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// StartWorker indicates an expected call of StartWorker. +func (mr *MockRunnerMockRecorder) StartWorker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorker", reflect.TypeOf((*MockRunner)(nil).StartWorker), arg0, arg1) +} + +// StopAndRemoveWorker mocks base method. +func (m *MockRunner) StopAndRemoveWorker(arg0 string, arg1 <-chan struct{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StopAndRemoveWorker", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// StopAndRemoveWorker indicates an expected call of StopAndRemoveWorker. +func (mr *MockRunnerMockRecorder) StopAndRemoveWorker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopAndRemoveWorker", reflect.TypeOf((*MockRunner)(nil).StopAndRemoveWorker), arg0, arg1) +} + +// Wait mocks base method. +func (m *MockRunner) Wait() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Wait") + ret0, _ := ret[0].(error) + return ret0 +} + +// Wait indicates an expected call of Wait. +func (mr *MockRunnerMockRecorder) Wait() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wait", reflect.TypeOf((*MockRunner)(nil).Wait)) +} + +// Worker mocks base method. +func (m *MockRunner) Worker(arg0 string, arg1 <-chan struct{}) (worker.Worker, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Worker", arg0, arg1) + ret0, _ := ret[0].(worker.Worker) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Worker indicates an expected call of Worker. +func (mr *MockRunnerMockRecorder) Worker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Worker", reflect.TypeOf((*MockRunner)(nil).Worker), arg0, arg1) +} diff --git a/worker/caasapplicationprovisioner/package_test.go b/worker/caasapplicationprovisioner/package_test.go index 6e31483243f..4aa7aae73ca 100644 --- a/worker/caasapplicationprovisioner/package_test.go +++ b/worker/caasapplicationprovisioner/package_test.go @@ -1,7 +1,7 @@ // Copyright 2020 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. -package caasapplicationprovisioner_test +package caasapplicationprovisioner import ( stdtesting "testing" @@ -12,3 +12,5 @@ import ( func TestPackage(t *stdtesting.T) { gc.TestingT(t) } + +var NewProvisionerWorkerForTest = newProvisionerWorker diff --git a/worker/caasapplicationprovisioner/worker.go b/worker/caasapplicationprovisioner/worker.go index 393c6a412a0..d0dc17c5ea8 100644 --- a/worker/caasapplicationprovisioner/worker.go +++ b/worker/caasapplicationprovisioner/worker.go @@ -70,6 +70,14 @@ type CAASBroker interface { Units(appName string, mode caas.DeploymentMode) ([]caas.Unit, error) } +// Runner exposes functionalities of a worker.Runner. +type Runner interface { + Worker(id string, abort <-chan struct{}) (worker.Worker, error) + StartWorker(id string, startFunc func() (worker.Worker, error)) error + StopAndRemoveWorker(id string, abort <-chan struct{}) error + worker.Worker +} + // Config defines the operation of a Worker. type Config struct { Facade CAASProvisionerFacade @@ -83,7 +91,7 @@ type Config struct { type provisioner struct { catacomb catacomb.Catacomb - runner *worker.Runner + runner Runner facade CAASProvisionerFacade broker CAASBroker clock clock.Clock @@ -95,6 +103,19 @@ type provisioner struct { // NewProvisionerWorker starts and returns a new CAAS provisioner worker. func NewProvisionerWorker(config Config) (worker.Worker, error) { + return newProvisionerWorker(config, + worker.NewRunner(worker.RunnerParams{ + Clock: config.Clock, + IsFatal: func(error) bool { return false }, + RestartDelay: 3 * time.Second, + Logger: config.Logger.Child("runner"), + }), + ) +} + +func newProvisionerWorker( + config Config, runner Runner, +) (worker.Worker, error) { p := &provisioner{ facade: config.Facade, broker: config.Broker, @@ -102,13 +123,8 @@ func NewProvisionerWorker(config Config) (worker.Worker, error) { clock: config.Clock, logger: config.Logger, newAppWorker: config.NewAppWorker, - runner: worker.NewRunner(worker.RunnerParams{ - Clock: config.Clock, - IsFatal: func(error) bool { return false }, - RestartDelay: 3 * time.Second, - Logger: config.Logger.Child("runner"), - }), - unitFacade: config.UnitFacade, + runner: runner, + unitFacade: config.UnitFacade, } err := catacomb.Invoke(catacomb.Plan{ Site: &p.catacomb, @@ -145,8 +161,17 @@ func (p *provisioner) loop() error { if !ok { return errors.New("app watcher closed channel") } - for _, app := range apps { - existingWorker, err := p.runner.Worker(app, nil) + for _, appName := range apps { + appLife, err := p.facade.Life(appName) + if err != nil && !errors.IsNotFound(err) { + return errors.Trace(err) + } + if errors.IsNotFound(err) || appLife == life.Dead { + p.shutDownAppWorker(appName) + continue + } + + existingWorker, err := p.runner.Worker(appName, p.catacomb.Dying()) if errors.IsNotFound(err) { // Ignore. } else if err == worker.ErrDead { @@ -163,16 +188,17 @@ func (p *provisioner) loop() error { } config := AppWorkerConfig{ - Name: app, + Name: appName, Facade: p.facade, Broker: p.broker, ModelTag: p.modelTag, Clock: p.clock, - Logger: p.logger.Child("applicationworker"), + Logger: p.logger.Child(appName), UnitFacade: p.unitFacade, } startFunc := p.newAppWorker(config) - err = p.runner.StartWorker(app, startFunc) + p.logger.Debugf("starting app worker %q", appName) + err = p.runner.StartWorker(appName, startFunc) if err != nil { return errors.Trace(err) } @@ -180,3 +206,15 @@ func (p *provisioner) loop() error { } } } + +func (p *provisioner) shutDownAppWorker(appName string) { + err := p.runner.StopAndRemoveWorker(appName, p.catacomb.Dying()) + if errors.IsNotFound(err) { + return + } + if err != nil { + p.logger.Warningf("stopping app worker %q: %v", appName, err) + return + } + p.logger.Debugf("removed app worker %q", appName) +} diff --git a/worker/caasapplicationprovisioner/worker_test.go b/worker/caasapplicationprovisioner/worker_test.go index 234632f3c14..574fe166143 100644 --- a/worker/caasapplicationprovisioner/worker_test.go +++ b/worker/caasapplicationprovisioner/worker_test.go @@ -6,7 +6,9 @@ package caasapplicationprovisioner_test import ( "time" + "github.com/golang/mock/gomock" "github.com/juju/clock/testclock" + "github.com/juju/errors" "github.com/juju/loggo" "github.com/juju/names/v4" jc "github.com/juju/testing/checkers" @@ -15,9 +17,12 @@ import ( gc "gopkg.in/check.v1" "github.com/juju/juju/caas" + "github.com/juju/juju/core/life" + "github.com/juju/juju/core/watcher" "github.com/juju/juju/core/watcher/watchertest" coretesting "github.com/juju/juju/testing" "github.com/juju/juju/worker/caasapplicationprovisioner" + "github.com/juju/juju/worker/caasapplicationprovisioner/mocks" ) var _ = gc.Suite(&CAASApplicationSuite{}) @@ -37,13 +42,34 @@ func (s *CAASApplicationSuite) SetUpTest(c *gc.C) { } func (s *CAASApplicationSuite) TestWorkerStart(c *gc.C) { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + appChan := make(chan []string, 1) - appChan <- []string{"application-test"} - facade := &mockFacade{ - appWatcher: watchertest.NewMockStringsWatcher(appChan), - } + done := make(chan struct{}) + + facade := mocks.NewMockCAASProvisionerFacade(ctrl) + runner := mocks.NewMockRunner(ctrl) + + facade.EXPECT().WatchApplications().DoAndReturn(func() (watcher.StringsWatcher, error) { + appChan <- []string{"application-test"} + return watchertest.NewMockStringsWatcher(appChan), nil + }) + facade.EXPECT().Life("application-test").Return(life.Alive, nil) + runner.EXPECT().Worker("application-test", gomock.Any()).Return(nil, errors.NotFoundf("")) + runner.EXPECT().StartWorker("application-test", gomock.Any()).DoAndReturn( + func(_ string, startFunc func() (worker.Worker, error)) error { + startFunc() + return nil + }, + ) + runner.EXPECT().Wait().AnyTimes().DoAndReturn(func() error { + <-done + return nil + }) + runner.EXPECT().Kill().AnyTimes() + called := false - workerChan := make(chan struct{}) newWorker := func(config caasapplicationprovisioner.AppWorkerConfig) func() (worker.Worker, error) { c.Assert(called, jc.IsFalse) called = true @@ -52,12 +78,67 @@ func (s *CAASApplicationSuite) TestWorkerStart(c *gc.C) { mc.AddExpr("_.Broker", gc.NotNil) mc.AddExpr("_.Clock", gc.NotNil) mc.AddExpr("_.Logger", gc.NotNil) + mc.AddExpr("_.ShutDownCleanUpFunc", gc.NotNil) c.Check(config, mc, caasapplicationprovisioner.AppWorkerConfig{ Name: "application-test", ModelTag: s.modelTag, }) return func() (worker.Worker, error) { - close(workerChan) + close(done) + return workertest.NewErrorWorker(nil), nil + } + } + config := caasapplicationprovisioner.Config{ + Facade: facade, + Broker: struct{ caas.Broker }{}, + ModelTag: s.modelTag, + Clock: s.clock, + Logger: s.logger, + NewAppWorker: newWorker, + } + provisioner, err := caasapplicationprovisioner.NewProvisionerWorkerForTest(config, runner) + c.Assert(err, jc.ErrorIsNil) + c.Assert(provisioner, gc.NotNil) + + select { + case <-done: + c.Assert(called, jc.IsTrue) + case <-time.After(coretesting.LongWait): + c.Fatalf("timed out waiting for worker to start") + } + + workertest.CleanKill(c, provisioner) +} + +func (s *CAASApplicationSuite) TestWorkerShutdownForDeadApp(c *gc.C) { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + + appChan := make(chan []string, 1) + done := make(chan struct{}) + + facade := mocks.NewMockCAASProvisionerFacade(ctrl) + runner := mocks.NewMockRunner(ctrl) + + facade.EXPECT().WatchApplications().DoAndReturn(func() (watcher.StringsWatcher, error) { + appChan <- []string{"application-test"} + return watchertest.NewMockStringsWatcher(appChan), nil + }) + facade.EXPECT().Life("application-test").DoAndReturn(func(appName string) (life.Value, error) { + return life.Dead, nil + }) + runner.EXPECT().StopAndRemoveWorker("application-test", gomock.Any()).DoAndReturn(func(string, <-chan struct{}) error { + close(done) + return errors.NotFoundf("") + }) + runner.EXPECT().Wait().AnyTimes().DoAndReturn(func() error { + <-done + return nil + }) + runner.EXPECT().Kill().AnyTimes() + + newWorker := func(config caasapplicationprovisioner.AppWorkerConfig) func() (worker.Worker, error) { + return func() (worker.Worker, error) { return workertest.NewErrorWorker(nil), nil } } @@ -69,32 +150,73 @@ func (s *CAASApplicationSuite) TestWorkerStart(c *gc.C) { Logger: s.logger, NewAppWorker: newWorker, } - provisioner, err := caasapplicationprovisioner.NewProvisionerWorker(config) + provisioner, err := caasapplicationprovisioner.NewProvisionerWorkerForTest(config, runner) c.Assert(err, jc.ErrorIsNil) c.Assert(provisioner, gc.NotNil) select { - case <-workerChan: + case <-done: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for worker to start") } - c.Assert(called, jc.IsTrue) workertest.CleanKill(c, provisioner) } func (s *CAASApplicationSuite) TestWorkerStartOnceNotify(c *gc.C) { - appChan := make(chan []string, 4) + ctrl := gomock.NewController(c) + defer ctrl.Finish() + + appChan := make(chan []string, 5) + done := make(chan struct{}) + appChan <- []string{"application-test"} appChan <- []string{"application-test"} appChan <- []string{"application-test"} appChan <- []string{"application-test"} - facade := &mockFacade{ - appWatcher: watchertest.NewMockStringsWatcher(appChan), - } - called := 0 - workerChan := make(chan struct{}) + appChan <- []string{"application-test"} + facade := mocks.NewMockCAASProvisionerFacade(ctrl) + runner := mocks.NewMockRunner(ctrl) + var notifyWorker = &mockNotifyWorker{Worker: workertest.NewErrorWorker(nil)} + + gomock.InOrder( + facade.EXPECT().WatchApplications().DoAndReturn(func() (watcher.StringsWatcher, error) { + return watchertest.NewMockStringsWatcher(appChan), nil + }), + facade.EXPECT().Life("application-test").Return(life.Alive, nil), + runner.EXPECT().Worker("application-test", gomock.Any()).Return(nil, errors.NotFoundf("")), + runner.EXPECT().StartWorker("application-test", gomock.Any()).DoAndReturn( + func(_ string, startFunc func() (worker.Worker, error)) error { + startFunc() + return nil + }, + ), + + facade.EXPECT().Life("application-test").Return(life.Alive, nil), + runner.EXPECT().Worker("application-test", gomock.Any()).Return(notifyWorker, nil), + + facade.EXPECT().Life("application-test").Return(life.Alive, nil), + runner.EXPECT().Worker("application-test", gomock.Any()).Return(notifyWorker, nil), + + facade.EXPECT().Life("application-test").Return(life.Alive, nil), + runner.EXPECT().Worker("application-test", gomock.Any()).Return(notifyWorker, nil), + + facade.EXPECT().Life("application-test").Return(life.Alive, nil), + runner.EXPECT().Worker("application-test", gomock.Any()).DoAndReturn( + func(_ string, abort <-chan struct{}) (worker.Worker, error) { + close(done) + return nil, worker.ErrDead + }, + ), + ) + runner.EXPECT().Wait().AnyTimes().DoAndReturn(func() error { + <-done + return nil + }) + runner.EXPECT().Kill().AnyTimes() + + called := 0 newWorker := func(config caasapplicationprovisioner.AppWorkerConfig) func() (worker.Worker, error) { called++ mc := jc.NewMultiChecker() @@ -102,12 +224,12 @@ func (s *CAASApplicationSuite) TestWorkerStartOnceNotify(c *gc.C) { mc.AddExpr("_.Broker", gc.NotNil) mc.AddExpr("_.Clock", gc.NotNil) mc.AddExpr("_.Logger", gc.NotNil) + mc.AddExpr("_.ShutDownCleanUpFunc", gc.NotNil) c.Check(config, mc, caasapplicationprovisioner.AppWorkerConfig{ Name: "application-test", ModelTag: s.modelTag, }) return func() (worker.Worker, error) { - close(workerChan) return notifyWorker, nil } } @@ -119,12 +241,12 @@ func (s *CAASApplicationSuite) TestWorkerStartOnceNotify(c *gc.C) { Logger: s.logger, NewAppWorker: newWorker, } - provisioner, err := caasapplicationprovisioner.NewProvisionerWorker(config) + provisioner, err := caasapplicationprovisioner.NewProvisionerWorkerForTest(config, runner) c.Assert(err, jc.ErrorIsNil) c.Assert(provisioner, gc.NotNil) select { - case <-workerChan: + case <-done: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for worker to start") } diff --git a/worker/caasfirewaller/worker_test.go b/worker/caasfirewaller/worker_test.go index 2a18ab9aae1..6f3b402ea02 100644 --- a/worker/caasfirewaller/worker_test.go +++ b/worker/caasfirewaller/worker_test.go @@ -11,6 +11,7 @@ import ( "github.com/juju/loggo" "github.com/juju/testing" jc "github.com/juju/testing/checkers" + "github.com/juju/utils/v2" "github.com/juju/worker/v3/workertest" gc "gopkg.in/check.v1" @@ -271,37 +272,39 @@ func (s *WorkerSuite) TestWatcherErrorStopsWorker(c *gc.C) { s.sendApplicationChange(c, "gitlab") s.applicationGetter.appWatcher.KillErr(errors.New("splat")) - workertest.CheckKilled(c, s.applicationGetter.appWatcher) - workertest.CheckKilled(c, s.applicationGetter.allWatcher) + _ = workertest.CheckKilled(c, s.applicationGetter.appWatcher) + _ = workertest.CheckKilled(c, s.applicationGetter.allWatcher) err = workertest.CheckKilled(c, w) c.Assert(err, gc.ErrorMatches, "splat") } func (s *WorkerSuite) TestV2CharmSkipProcessing(c *gc.C) { - w, err := caasfirewaller.NewWorker(s.config) - c.Assert(err, jc.ErrorIsNil) - defer workertest.CleanKill(c, w) - s.charmGetter.charmInfo.Manifest = &charm.Manifest{Bases: []charm.Base{{}}} s.charmGetter.charmInfo.Meta = &charm.Meta{} + w, err := caasfirewaller.NewWorker(s.config) + c.Assert(err, jc.ErrorIsNil) + s.sendApplicationChange(c, "gitlab") + s.waitCharmGetterCalls(c, "ApplicationCharmInfo") + + workertest.CleanKill(c, w) - s.charmGetter.CheckCallNames(c, "ApplicationCharmInfo") - s.lifeGetter.CheckNoCalls(c) + s.expectNoLifeGetterCalls(c) } func (s *WorkerSuite) TestCharmNotFound(c *gc.C) { w, err := caasfirewaller.NewWorker(s.config) c.Assert(err, jc.ErrorIsNil) - defer workertest.CleanKill(c, w) s.charmGetter.charmInfo = nil s.sendApplicationChange(c, "gitlab") + s.waitCharmGetterCalls(c, "ApplicationCharmInfo") - s.charmGetter.CheckCallNames(c, "ApplicationCharmInfo") - s.lifeGetter.CheckNoCalls(c) + workertest.CleanKill(c, w) + + s.expectNoLifeGetterCalls(c) } func (s *WorkerSuite) TestCharmChangesToV2(c *gc.C) { @@ -311,7 +314,7 @@ func (s *WorkerSuite) TestCharmChangesToV2(c *gc.C) { s.sendApplicationChange(c, "gitlab") s.waitCharmGetterCalls(c, "ApplicationCharmInfo") - s.lifeGetter.CheckCallNames(c, "Life") + s.waitLifeGetterCalls(c, "Life") s.charmGetter.charmInfo.Manifest = &charm.Manifest{Bases: []charm.Base{{}}} s.charmGetter.charmInfo.Meta = &charm.Meta{} @@ -323,11 +326,37 @@ func (s *WorkerSuite) TestCharmChangesToV2(c *gc.C) { } func (s *WorkerSuite) waitCharmGetterCalls(c *gc.C, names ...string) { + waitStubCalls(c, &s.charmGetter, names...) +} + +func (s *WorkerSuite) waitLifeGetterCalls(c *gc.C, names ...string) { + waitStubCalls(c, &s.lifeGetter, names...) +} + +type waitStub interface { + Calls() []testing.StubCall + CheckCallNames(c *gc.C, expected ...string) bool + ResetCalls() +} + +func waitStubCalls(c *gc.C, stub waitStub, names ...string) { for a := coretesting.LongAttempt.Start(); a.Next(); { - if len(s.charmGetter.Calls()) >= len(names) { + if len(stub.Calls()) >= len(names) { break } } - s.charmGetter.CheckCallNames(c, names...) - s.charmGetter.ResetCalls() + stub.CheckCallNames(c, names...) + stub.ResetCalls() +} + +func (s *WorkerSuite) expectNoLifeGetterCalls(c *gc.C) { + strategy := utils.AttemptStrategy{ + Total: coretesting.ShortWait, + Delay: 10 * time.Millisecond, + } + for a := strategy.Start(); a.Next(); { + if len(s.lifeGetter.Calls()) > 0 { + c.Fatalf("unexpected lifegetter call") + } + } } diff --git a/worker/caasunitprovisioner/mock_test.go b/worker/caasunitprovisioner/mock_test.go index 0cb83e7ac5e..c9a2dac9ede 100644 --- a/worker/caasunitprovisioner/mock_test.go +++ b/worker/caasunitprovisioner/mock_test.go @@ -68,7 +68,10 @@ func (m *mockServiceBroker) GetService(appName string, mode caas.DeploymentMode, m.MethodCall(m, "GetService", appName, mode) scale := 4 return &caas.Service{ - Id: "id", Scale: &scale, Addresses: network.NewProviderAddresses("10.0.0.1"), Status: m.serviceStatus, + Id: "id", + Scale: &scale, + Addresses: network.NewMachineAddresses([]string{"10.0.0.1"}).AsProviderAddresses(), + Status: m.serviceStatus, }, m.NextErr() } diff --git a/worker/caasunitprovisioner/worker_test.go b/worker/caasunitprovisioner/worker_test.go index 54fee5ba54a..fe51339328e 100644 --- a/worker/caasunitprovisioner/worker_test.go +++ b/worker/caasunitprovisioner/worker_test.go @@ -419,7 +419,7 @@ func (s *WorkerSuite) TestScaleChangedInCluster(c *gc.C) { params.UpdateApplicationServiceArg{ ApplicationTag: names.NewApplicationTag("gitlab").String(), ProviderId: "id", - Addresses: params.FromProviderAddresses(network.NewProviderAddresses("10.0.0.1")...), + Addresses: params.FromProviderAddresses(network.NewMachineAddresses([]string{"10.0.0.1"}).AsProviderAddresses()...), Scale: intPtr(4), }, }) diff --git a/worker/charmrevision/manifold.go b/worker/charmrevision/manifold.go index 37955a766b5..ea061f46322 100644 --- a/worker/charmrevision/manifold.go +++ b/worker/charmrevision/manifold.go @@ -68,7 +68,9 @@ func Manifold(config ManifoldConfig) dependency.Manifold { } // NewAPIFacade returns a Facade backed by the supplied APICaller. -func NewAPIFacade(apiCaller base.APICaller) (Facade, error) { +var NewAPIFacade = newAPIFacade + +func newAPIFacade(apiCaller base.APICaller) (Facade, error) { return charmrevisionupdater.NewClient(apiCaller), nil } diff --git a/worker/instancepoller/worker_test.go b/worker/instancepoller/worker_test.go index 20e5aa077d3..4aa9c42ac81 100644 --- a/worker/instancepoller/worker_test.go +++ b/worker/instancepoller/worker_test.go @@ -36,10 +36,12 @@ var ( _ = gc.Suite(&workerSuite{}) testAddrs = network.ProviderAddresses{ - network.NewProviderAddress( - "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal)), - network.NewProviderAddress( - "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic)), + network.NewMachineAddress( + "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal), + ).AsProviderAddress(), + network.NewMachineAddress( + "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic), + ).AsProviderAddress(), } testNetIfs = network.InterfaceInfos{ @@ -48,12 +50,14 @@ var ( InterfaceName: "eth0", MACAddress: "de:ad:be:ef:00:00", Addresses: network.ProviderAddresses{ - network.NewProviderAddress( - "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress( + "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal), + ).AsProviderAddress(), }, ShadowAddresses: network.ProviderAddresses{ - network.NewProviderAddress( - "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic)), + network.NewMachineAddress( + "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic), + ).AsProviderAddress(), }, }, } @@ -62,15 +66,17 @@ var ( { DeviceIndex: 0, Addresses: network.ProviderAddresses{ - network.NewProviderAddress( - "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal)), + network.NewMachineAddress( + "10.0.0.1", network.WithCIDR("10.0.0.0/24"), network.WithScope(network.ScopeCloudLocal), + ).AsProviderAddress(), }, }, { DeviceIndex: 1, ShadowAddresses: network.ProviderAddresses{ - network.NewProviderAddress( - "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic)), + network.NewMachineAddress( + "1.1.1.42", network.WithCIDR("1.1.1.0/24"), network.WithScope(network.ScopePublic), + ).AsProviderAddress(), }, }, } diff --git a/worker/machiner/machiner.go b/worker/machiner/machiner.go index a0e05ebc3a6..6dcd7c028f0 100644 --- a/worker/machiner/machiner.go +++ b/worker/machiner/machiner.go @@ -146,7 +146,7 @@ func setMachineAddresses(tag names.MachineTag, m Machine) error { if cidr := corenetwork.NetworkCIDRFromIPAndMask(ip, netmask); cidr != "" { addrOpts = append(addrOpts, corenetwork.WithCIDR(cidr)) } - address := corenetwork.NewProviderAddress(ip.String(), addrOpts...) + address := corenetwork.NewMachineAddress(ip.String(), addrOpts...).AsProviderAddress() // Filter out link-local addresses as we cannot reliably use them. if address.Scope == corenetwork.ScopeLinkLocal {