diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 1d782d92dff..2a6e66531c4 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -1,7 +1,7 @@ name: Merge on: push: - branches: ['2.9', '3.1', '3.2', '3.3'] + branches: ['2.9', '3.1', '3.3', '3.4'] jobs: check-merge: @@ -10,9 +10,9 @@ jobs: env: MERGE_TARGETS: | 2.9: 3.1 - 3.1: 3.2 - 3.2: 3.3 - 3.3: main + 3.1: 3.3 + 3.3: 3.4 + 3.4: main steps: - name: Determine source/target branches @@ -54,7 +54,7 @@ jobs: # Need to set Git username/email to do the merge (yawn) git config user.name 'jujubot' git config user.email 'fake@address.me' - + set +e git switch "$TARGET_BRANCH" git merge "$SOURCE_BRANCH" diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index a3896add50f..ea38f35d808 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,8 @@ -*Why this change is needed and what it does.* + ## Checklist -*If an item is not applicable, use `~strikethrough~`.* + - [ ] Code style: imports ordered, good names, simple structure, etc - [ ] Comments saying why design decisions were made @@ -12,20 +12,17 @@ ## QA steps -*Commands to run to verify that the change works.* - -```sh -QA steps here -``` + ## Documentation changes -*How it affects user workflow (CLI or API). Delete section if not applicable.* + ## Links -**Launchpad bug:** https://pad.lv/ + + +**Launchpad bug:** https://bugs.launchpad.net/juju/+bug/ -**Jira card:** JUJU-[XXXX] +**Jira card:** JUJU- -*Insert other relevant links here.* \ No newline at end of file diff --git a/api/agent/uniter/unit.go b/api/agent/uniter/unit.go index 1206b3dece1..28279aaaf41 100644 --- a/api/agent/uniter/unit.go +++ b/api/agent/uniter/unit.go @@ -1027,6 +1027,15 @@ func (b *CommitHookParamsBuilder) AddSecretUpdates(updates []SecretUpsertArg) { } } +// AddTrackLatest records the URIs for which the latest revision should be tracked. +func (b *CommitHookParamsBuilder) AddTrackLatest(trackLatest []string) { + if len(trackLatest) == 0 { + return + } + b.arg.TrackLatest = make([]string, len(trackLatest)) + copy(b.arg.TrackLatest, trackLatest) +} + // SecretGrantRevokeArgs holds parameters for updating a secret's access. type SecretGrantRevokeArgs struct { URI *secrets.URI diff --git a/api/apiclient.go b/api/apiclient.go index 6613978ae7e..8940794fa76 100644 --- a/api/apiclient.go +++ b/api/apiclient.go @@ -35,6 +35,7 @@ import ( "gopkg.in/retry.v1" "github.com/juju/juju/api/base" + "github.com/juju/juju/core/facades" coremacaroon "github.com/juju/juju/core/macaroon" "github.com/juju/juju/core/network" jujuproxy "github.com/juju/juju/internal/proxy" @@ -247,7 +248,7 @@ func Open(info *Info, opts DialOpts) (Connection, error) { host = dialResult.addr } - pingerFacadeVersions := FacadeVersions["Pinger"] + pingerFacadeVersions := facadeVersions["Pinger"] if len(pingerFacadeVersions) == 0 { return nil, errors.Errorf("pinger facade version is required") } @@ -1365,7 +1366,7 @@ func (c *conn) PublicDNSName() string { // Facade we will want to use. It needs to line up the versions that the server // reports to us, with the versions that our client knows how to use. func (c *conn) BestFacadeVersion(facade string) int { - return bestVersion(FacadeVersions[facade], c.facadeVersions[facade]) + return facades.BestVersion(facadeVersions[facade], c.facadeVersions[facade]) } // serverRoot returns the cached API server address and port used diff --git a/api/controller/caasapplicationprovisioner/client.go b/api/controller/caasapplicationprovisioner/client.go index d2cb9758c20..37537f42d53 100644 --- a/api/controller/caasapplicationprovisioner/client.go +++ b/api/controller/caasapplicationprovisioner/client.go @@ -422,7 +422,6 @@ func (c *Client) DestroyUnits(unitNames []string) error { args := params.DestroyUnitsParams{} args.Units = make([]params.DestroyUnitParams, 0, len(unitNames)) - fmt.Println(unitNames) for _, unitName := range unitNames { tag := names.NewUnitTag(unitName) args.Units = append(args.Units, params.DestroyUnitParams{ diff --git a/api/controller/migrationmaster/client_test.go b/api/controller/migrationmaster/client_test.go index 726587956cc..627321736df 100644 --- a/api/controller/migrationmaster/client_test.go +++ b/api/controller/migrationmaster/client_test.go @@ -59,7 +59,7 @@ func (s *ClientSuite) TestWatch(c *gc.C) { w, err := client.Watch() c.Check(err, jc.ErrorIsNil) c.Check(w, gc.Equals, expectWatch) - stub.CheckCalls(c, []jujutesting.StubCall{{"MigrationMaster.Watch", []interface{}{"", nil}}}) + stub.CheckCalls(c, []jujutesting.StubCall{{FuncName: "MigrationMaster.Watch", Args: []interface{}{"", nil}}}) } func (s *ClientSuite) TestWatchCallError(c *gc.C) { @@ -136,7 +136,7 @@ func (s *ClientSuite) TestSetPhase(c *gc.C) { c.Assert(err, jc.ErrorIsNil) expectedArg := params.SetMigrationPhaseArgs{Phase: "QUIESCE"} stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.SetPhase", []interface{}{"", expectedArg}}, + {FuncName: "MigrationMaster.SetPhase", Args: []interface{}{"", expectedArg}}, }) } @@ -160,7 +160,7 @@ func (s *ClientSuite) TestSetStatusMessage(c *gc.C) { c.Assert(err, jc.ErrorIsNil) expectedArg := params.SetMigrationStatusMessageArgs{Message: "foo"} stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.SetStatusMessage", []interface{}{"", expectedArg}}, + {FuncName: "MigrationMaster.SetStatusMessage", Args: []interface{}{"", expectedArg}}, }) } @@ -190,7 +190,7 @@ func (s *ClientSuite) TestModelInfo(c *gc.C) { client := migrationmaster.NewClient(apiCaller, nil) model, err := client.ModelInfo() stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.ModelInfo", []interface{}{"", nil}}, + {FuncName: "MigrationMaster.ModelInfo", Args: []interface{}{"", nil}}, }) c.Check(err, jc.ErrorIsNil) c.Check(model, jc.DeepEquals, migration.ModelInfo{ @@ -218,7 +218,7 @@ func (s *ClientSuite) TestSourceControllerInfo(c *gc.C) { client := migrationmaster.NewClient(apiCaller, nil) info, relatedModels, err := client.SourceControllerInfo() stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.SourceControllerInfo", []interface{}{"", nil}}, + {FuncName: "MigrationMaster.SourceControllerInfo", Args: []interface{}{"", nil}}, }) c.Check(err, jc.ErrorIsNil) c.Check(info, jc.DeepEquals, migration.SourceControllerInfo{ @@ -241,7 +241,7 @@ func (s *ClientSuite) TestPrechecks(c *gc.C) { c.Check(err, gc.ErrorMatches, "blam") expectedArg := params.PrechecksArgs{} stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.Prechecks", []interface{}{"", expectedArg}}, + {FuncName: "MigrationMaster.Prechecks", Args: []interface{}{"", expectedArg}}, }) } @@ -332,7 +332,7 @@ func (s *ClientSuite) TestExport(c *gc.C) { out, err := client.Export() c.Assert(err, jc.ErrorIsNil) stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.Export", []interface{}{"", nil}}, + {FuncName: "MigrationMaster.Export", Args: []interface{}{"", nil}}, }) c.Assert(out, gc.DeepEquals, migration.SerializedModel{ Bytes: []byte("foo"), @@ -441,7 +441,7 @@ func (s *ClientSuite) TestReap(c *gc.C) { err := client.Reap() c.Check(err, jc.ErrorIsNil) stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.Reap", []interface{}{"", nil}}, + {FuncName: "MigrationMaster.Reap", Args: []interface{}{"", nil}}, }) } @@ -474,7 +474,7 @@ func (s *ClientSuite) TestWatchMinionReports(c *gc.C) { w, err := client.WatchMinionReports() c.Check(err, jc.ErrorIsNil) c.Check(w, gc.Equals, expectWatch) - stub.CheckCalls(c, []jujutesting.StubCall{{"MigrationMaster.WatchMinionReports", []interface{}{"", nil}}}) + stub.CheckCalls(c, []jujutesting.StubCall{{FuncName: "MigrationMaster.WatchMinionReports", Args: []interface{}{"", nil}}}) } func (s *ClientSuite) TestWatchMinionReportsError(c *gc.C) { @@ -515,7 +515,7 @@ func (s *ClientSuite) TestMinionReports(c *gc.C) { out, err := client.MinionReports() c.Assert(err, jc.ErrorIsNil) stub.CheckCalls(c, []jujutesting.StubCall{ - {"MigrationMaster.MinionReports", []interface{}{"", nil}}, + {FuncName: "MigrationMaster.MinionReports", Args: []interface{}{"", nil}}, }) c.Assert(out, gc.DeepEquals, migration.MinionReports{ MigrationId: "id", diff --git a/api/controller/migrationtarget/client.go b/api/controller/migrationtarget/client.go index 8fdf0f87eb5..3c756b12b78 100644 --- a/api/controller/migrationtarget/client.go +++ b/api/controller/migrationtarget/client.go @@ -18,6 +18,7 @@ import ( "github.com/juju/version/v2" "gopkg.in/httprequest.v1" + "github.com/juju/juju/api" "github.com/juju/juju/api/base" coremigration "github.com/juju/juju/core/migration" "github.com/juju/juju/core/resources" @@ -55,13 +56,26 @@ func (c *Client) BestFacadeVersion() int { return c.caller.BestAPIVersion() } +// Prechecks checks that the target controller is able to accept the +// model being migrated. func (c *Client) Prechecks(model coremigration.ModelInfo) error { + // Pass all the known facade versions to the controller so that it + // can check that the target controller supports them. Passing all of them + // ensures that we don't have to update this code when new facades are + // added, or if the controller wants to change the logic service side. + supported := api.SupportedFacadeVersions() + versions := make(map[string][]int, len(supported)) + for name, version := range supported { + versions[name] = version + } + args := params.MigrationModelInfo{ UUID: model.UUID, Name: model.Name, OwnerTag: model.Owner.String(), AgentVersion: model.AgentVersion, ControllerAgentVersion: model.ControllerAgentVersion, + FacadeVersions: versions, } return errors.Trace(c.caller.FacadeCall(context.TODO(), "Prechecks", args, nil)) } diff --git a/api/controller/migrationtarget/client_test.go b/api/controller/migrationtarget/client_test.go index 60aa218192e..2d7f4a043a5 100644 --- a/api/controller/migrationtarget/client_test.go +++ b/api/controller/migrationtarget/client_test.go @@ -74,9 +74,14 @@ func (s *ClientSuite) TestPrechecks(c *gc.C) { AgentVersion: vers, ControllerAgentVersion: controllerVers, } - stub.CheckCalls(c, []jujutesting.StubCall{ - {FuncName: "MigrationTarget.Prechecks", Args: []interface{}{"", expectedArg}}, - }) + stub.CheckCallNames(c, "MigrationTarget.Prechecks") + + arg := stub.Calls()[0].Args[1].(params.MigrationModelInfo) + + mc := jc.NewMultiChecker() + mc.AddExpr("_.FacadeVersions", gc.Not(gc.HasLen), 0) + + c.Assert(arg, mc, expectedArg) } func (s *ClientSuite) TestImport(c *gc.C) { diff --git a/api/export_test.go b/api/export_test.go index 20b0c72f322..bf43b0f8ef6 100644 --- a/api/export_test.go +++ b/api/export_test.go @@ -19,7 +19,7 @@ import ( var ( CertDir = &certDir SlideAddressToFront = slideAddressToFront - BestVersion = bestVersion + FacadeVersions = &facadeVersions ) func DialAPI(info *Info, opts DialOpts) (jsoncodec.JSONConn, string, error) { diff --git a/api/facadeversions.go b/api/facadeversions.go index 4a7c4b6e92a..db974ba3e8c 100644 --- a/api/facadeversions.go +++ b/api/facadeversions.go @@ -3,7 +3,12 @@ package api -import "github.com/juju/collections/set" +import "github.com/juju/juju/core/facades" + +// SupportedFacadeVersions returns the list of facades that the api supports. +func SupportedFacadeVersions() facades.FacadeVersions { + return facadeVersions +} // facadeVersions lists the best version of facades that we want to support. This // will be used to pick out a default version for communication, given the list @@ -15,7 +20,7 @@ import "github.com/juju/collections/set" // FullStatus (client facade) and Migration (controller facade) is needed. // New facades should start at 1. // We no longer support facade versions at 0. -var FacadeVersions = map[string][]int{ +var facadeVersions = facades.FacadeVersions{ "Action": {7}, "ActionPruner": {1}, "Agent": {3}, @@ -83,7 +88,7 @@ var FacadeVersions = map[string][]int{ "MigrationMaster": {3}, "MigrationMinion": {1}, "MigrationStatusWatcher": {1}, - "MigrationTarget": {1, 2}, + "MigrationTarget": {1, 2, 3}, "ModelConfig": {3}, "ModelGeneration": {4}, "ModelManager": {9, 10}, @@ -132,14 +137,3 @@ var FacadeVersions = map[string][]int{ "VolumeAttachmentsWatcher": {2}, "VolumeAttachmentPlansWatcher": {1}, } - -// bestVersion tries to find the newest version in the version list that we can -// use. -func bestVersion(desired []int, versions []int) int { - intersection := set.NewInts(desired...).Intersection(set.NewInts(versions...)) - if intersection.Size() == 0 { - return 0 - } - sorted := intersection.SortedValues() - return sorted[len(sorted)-1] -} diff --git a/api/facadeversions_test.go b/api/facadeversions_test.go index 33521988fa3..c9e653916b0 100644 --- a/api/facadeversions_test.go +++ b/api/facadeversions_test.go @@ -7,6 +7,7 @@ import ( gc "gopkg.in/check.v1" "github.com/juju/juju/api" + "github.com/juju/juju/core/facades" coretesting "github.com/juju/juju/testing" ) @@ -16,8 +17,8 @@ type facadeVersionSuite struct { var _ = gc.Suite(&facadeVersionSuite{}) -func checkBestVersion(c *gc.C, desiredVersion []int, versions []int, expectedVersion int) { - resultVersion := api.BestVersion(desiredVersion, versions) +func checkBestVersion(c *gc.C, desiredVersion, versions []int, expectedVersion int) { + resultVersion := facades.BestVersion(desiredVersion, versions) c.Check(resultVersion, gc.Equals, expectedVersion) } @@ -51,7 +52,7 @@ func (*facadeVersionSuite) TestBestVersionNotSorted(c *gc.C) { } func (s *facadeVersionSuite) TestBestFacadeVersionExactMatch(c *gc.C) { - s.PatchValue(&api.FacadeVersions, map[string][]int{"Client": {1}}) + s.PatchValue(api.FacadeVersions, map[string]facades.FacadeVersion{"Client": {1}}) conn := api.NewTestingConnection(api.TestingConnectionParams{ FacadeVersions: map[string][]int{ "Client": {0, 1}, @@ -60,7 +61,7 @@ func (s *facadeVersionSuite) TestBestFacadeVersionExactMatch(c *gc.C) { } func (s *facadeVersionSuite) TestBestFacadeVersionNewerServer(c *gc.C) { - s.PatchValue(&api.FacadeVersions, map[string][]int{"Client": {1}}) + s.PatchValue(api.FacadeVersions, map[string]facades.FacadeVersion{"Client": {1}}) conn := api.NewTestingConnection(api.TestingConnectionParams{ FacadeVersions: map[string][]int{ "Client": {0, 1, 2}, @@ -69,7 +70,7 @@ func (s *facadeVersionSuite) TestBestFacadeVersionNewerServer(c *gc.C) { } func (s *facadeVersionSuite) TestBestFacadeVersionNewerClient(c *gc.C) { - s.PatchValue(&api.FacadeVersions, map[string][]int{"Client": {1, 2}}) + s.PatchValue(api.FacadeVersions, map[string]facades.FacadeVersion{"Client": {1, 2}}) conn := api.NewTestingConnection(api.TestingConnectionParams{ FacadeVersions: map[string][]int{ "Client": {0, 1}, @@ -78,7 +79,7 @@ func (s *facadeVersionSuite) TestBestFacadeVersionNewerClient(c *gc.C) { } func (s *facadeVersionSuite) TestBestFacadeVersionServerUnknown(c *gc.C) { - s.PatchValue(&api.FacadeVersions, map[string][]int{"TestingAPI": {1, 2}}) + s.PatchValue(api.FacadeVersions, map[string]facades.FacadeVersion{"TestingAPI": {1, 2}}) conn := api.NewTestingConnection(api.TestingConnectionParams{ FacadeVersions: map[string][]int{ "Client": {0, 1}, diff --git a/api/package_test.go b/api/package_test.go index 49620ef250d..b84f687fb1a 100644 --- a/api/package_test.go +++ b/api/package_test.go @@ -33,6 +33,7 @@ func (*ImportSuite) TestImports(c *gc.C) { "core/constraints", "core/database", "core/devices", + "core/facades", "core/instance", "core/life", "core/macaroon", diff --git a/apiserver/allfacades.go b/apiserver/allfacades.go index 782f70c6214..60445b4e0f1 100644 --- a/apiserver/allfacades.go +++ b/apiserver/allfacades.go @@ -44,9 +44,9 @@ import ( "github.com/juju/juju/apiserver/facades/client/action" "github.com/juju/juju/apiserver/facades/client/annotations" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/application" - "github.com/juju/juju/apiserver/facades/client/applicationoffers" - "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/applicationoffers" // ModelUser Write + "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/charms" // ModelUser Write "github.com/juju/juju/apiserver/facades/client/client" // ModelUser Write @@ -105,8 +105,74 @@ import ( "github.com/juju/juju/apiserver/facades/controller/undertaker" "github.com/juju/juju/apiserver/facades/controller/usersecrets" "github.com/juju/juju/apiserver/facades/controller/usersecretsdrain" + "github.com/juju/juju/core/facades" ) +// requiredMigrationFacadeVersions returns the facade versions that +// must be available for the migration master to function. +// This is a separate function so that it can be used in the +// migrationmaster facade registration as a dependency. +// +// A lot of the agent facades aren't actually required, but they are +// included here to keep the agent alive during migration. +func requiredMigrationFacadeVersions() facades.FacadeVersions { + registry := new(facade.Registry) + + // Client and modelmanager facades are required for the migration + // master to function correctly. Missing a model manager causes the + // status to error out. + client.Register(registry) + modelmanager.Register(registry) + + // The following are required to keep the agent alive during + // migration. + // This list is extremely conservative, and should be trimmed down + // once we have a better idea of what is actually required. + agent.Register(registry) + caasadmission.Register(registry) + caasagent.Register(registry) + caasapplication.Register(registry) + credentialvalidator.Register(registry) + deployer.Register(registry) + diskmanager.Register(registry) + fanconfigurer.Register(registry) + hostkeyreporter.Register(registry) + instancemutater.Register(registry) + keyupdater.Register(registry) + leadership.Register(registry) + agentlifeflag.Register(registry) + loggerapi.Register(registry) + machine.Register(registry) + machineactions.Register(registry) + meterstatus.Register(registry) + metricsadder.Register(registry) + migrationflag.Register(registry) + migrationminion.Register(registry) + payloadshookcontext.Register(registry) + provisioner.Register(registry) + proxyupdater.Register(registry) + reboot.Register(registry) + resourceshookcontext.Register(registry) + retrystrategy.Register(registry) + secretsdrain.Register(registry) + secretsmanager.Register(registry) + storageprovisioner.Register(registry) + unitassigner.Register(registry) + uniter.Register(registry) + upgrader.Register(registry) + upgradeseries.Register(registry) + upgradesteps.Register(registry) + + registerWatchers(registry) + + list := registry.List() + versions := make(facades.FacadeVersions, len(list)) + for _, details := range list { + versions[details.Name] = details.Versions + } + return versions +} + // AllFacades returns a registry containing all known API facades. // // This will panic if facade registration fails, but there is a unit @@ -179,7 +245,7 @@ func AllFacades() *facade.Registry { migrationflag.Register(registry) migrationmaster.Register(registry) migrationminion.Register(registry) - migrationtarget.Register(registry) + migrationtarget.Register(requiredMigrationFacadeVersions())(registry) modelconfig.Register(registry) modelgeneration.Register(registry) modelmanager.Register(registry) @@ -216,27 +282,7 @@ func AllFacades() *facade.Registry { upgradesteps.Register(registry) usermanager.Register(registry) - registry.MustRegister("AllWatcher", 3, NewAllWatcher, reflect.TypeOf((*SrvAllWatcher)(nil))) - // Note: AllModelWatcher uses the same infrastructure as AllWatcher - // but they are get under separate names as it possible the may - // diverge in the future (especially in terms of authorisation - // checks). - registry.MustRegister("AllModelWatcher", 4, NewAllWatcher, reflect.TypeOf((*SrvAllWatcher)(nil))) - registry.MustRegister("NotifyWatcher", 1, newNotifyWatcher, reflect.TypeOf((*srvNotifyWatcher)(nil))) - registry.MustRegister("StringsWatcher", 1, newStringsWatcher, reflect.TypeOf((*srvStringsWatcher)(nil))) - registry.MustRegister("OfferStatusWatcher", 1, newOfferStatusWatcher, reflect.TypeOf((*srvOfferStatusWatcher)(nil))) - registry.MustRegister("RelationStatusWatcher", 1, newRelationStatusWatcher, reflect.TypeOf((*srvRelationStatusWatcher)(nil))) - registry.MustRegister("RelationUnitsWatcher", 1, newRelationUnitsWatcher, reflect.TypeOf((*srvRelationUnitsWatcher)(nil))) - registry.MustRegister("RemoteRelationWatcher", 1, newRemoteRelationWatcher, reflect.TypeOf((*srvRemoteRelationWatcher)(nil))) - registry.MustRegister("VolumeAttachmentsWatcher", 2, newVolumeAttachmentsWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) - registry.MustRegister("VolumeAttachmentPlansWatcher", 1, newVolumeAttachmentPlansWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) - registry.MustRegister("FilesystemAttachmentsWatcher", 2, newFilesystemAttachmentsWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) - registry.MustRegister("EntityWatcher", 2, newEntitiesWatcher, reflect.TypeOf((*srvEntitiesWatcher)(nil))) - registry.MustRegister("MigrationStatusWatcher", 1, newMigrationStatusWatcher, reflect.TypeOf((*srvMigrationStatusWatcher)(nil))) - registry.MustRegister("ModelSummaryWatcher", 1, newModelSummaryWatcher, reflect.TypeOf((*SrvModelSummaryWatcher)(nil))) - registry.MustRegister("SecretsTriggerWatcher", 1, newSecretsTriggerWatcher, reflect.TypeOf((*srvSecretTriggerWatcher)(nil))) - registry.MustRegister("SecretBackendsRotateWatcher", 1, newSecretBackendsRotateWatcher, reflect.TypeOf((*srvSecretBackendsRotateWatcher)(nil))) - registry.MustRegister("SecretsRevisionWatcher", 1, newSecretsRevisionWatcher, reflect.TypeOf((*srvSecretsRevisionWatcher)(nil))) + registerWatchers(registry) return registry } @@ -263,3 +309,27 @@ func AdminFacadeDetails() []facade.Details { } return fs } + +func registerWatchers(registry *facade.Registry) { + registry.MustRegister("AllWatcher", 3, NewAllWatcher, reflect.TypeOf((*SrvAllWatcher)(nil))) + // Note: AllModelWatcher uses the same infrastructure as AllWatcher + // but they are get under separate names as it possible the may + // diverge in the future (especially in terms of authorisation + // checks). + registry.MustRegister("AllModelWatcher", 4, NewAllWatcher, reflect.TypeOf((*SrvAllWatcher)(nil))) + registry.MustRegister("NotifyWatcher", 1, newNotifyWatcher, reflect.TypeOf((*srvNotifyWatcher)(nil))) + registry.MustRegister("StringsWatcher", 1, newStringsWatcher, reflect.TypeOf((*srvStringsWatcher)(nil))) + registry.MustRegister("OfferStatusWatcher", 1, newOfferStatusWatcher, reflect.TypeOf((*srvOfferStatusWatcher)(nil))) + registry.MustRegister("RelationStatusWatcher", 1, newRelationStatusWatcher, reflect.TypeOf((*srvRelationStatusWatcher)(nil))) + registry.MustRegister("RelationUnitsWatcher", 1, newRelationUnitsWatcher, reflect.TypeOf((*srvRelationUnitsWatcher)(nil))) + registry.MustRegister("RemoteRelationWatcher", 1, newRemoteRelationWatcher, reflect.TypeOf((*srvRemoteRelationWatcher)(nil))) + registry.MustRegister("VolumeAttachmentsWatcher", 2, newVolumeAttachmentsWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) + registry.MustRegister("VolumeAttachmentPlansWatcher", 1, newVolumeAttachmentPlansWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) + registry.MustRegister("FilesystemAttachmentsWatcher", 2, newFilesystemAttachmentsWatcher, reflect.TypeOf((*srvMachineStorageIdsWatcher)(nil))) + registry.MustRegister("EntityWatcher", 2, newEntitiesWatcher, reflect.TypeOf((*srvEntitiesWatcher)(nil))) + registry.MustRegister("MigrationStatusWatcher", 1, newMigrationStatusWatcher, reflect.TypeOf((*srvMigrationStatusWatcher)(nil))) + registry.MustRegister("ModelSummaryWatcher", 1, newModelSummaryWatcher, reflect.TypeOf((*SrvModelSummaryWatcher)(nil))) + registry.MustRegister("SecretsTriggerWatcher", 1, newSecretsTriggerWatcher, reflect.TypeOf((*srvSecretTriggerWatcher)(nil))) + registry.MustRegister("SecretBackendsRotateWatcher", 1, newSecretBackendsRotateWatcher, reflect.TypeOf((*srvSecretBackendsRotateWatcher)(nil))) + registry.MustRegister("SecretsRevisionWatcher", 1, newSecretsRevisionWatcher, reflect.TypeOf((*srvSecretsRevisionWatcher)(nil))) +} diff --git a/apiserver/allfacades_test.go b/apiserver/allfacades_test.go index e9e41642183..d211ffbc8da 100644 --- a/apiserver/allfacades_test.go +++ b/apiserver/allfacades_test.go @@ -1,13 +1,15 @@ // Copyright 2017 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. -package apiserver_test +package apiserver import ( + "sort" + + "github.com/juju/collections/set" "github.com/juju/testing" + jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" - - "github.com/juju/juju/apiserver" ) type AllFacadesSuite struct { @@ -18,6 +20,38 @@ var _ = gc.Suite(&AllFacadesSuite{}) func (s *AllFacadesSuite) TestNoPanic(c *gc.C) { // AllFacades will panic on error so check it by calling it. - r := apiserver.AllFacades() + r := AllFacades() c.Assert(r, gc.NotNil) } + +func (s *AllFacadesSuite) TestFacadeVersionsInSync(c *gc.C) { + // Ensure that there is a complete overlap between the registered + // facade versions and the required facade versions. + facadeVersions := requiredMigrationFacadeVersions() + registeredFacades := AllFacades().List() + + m := make(map[string]set.Ints) + for _, desc := range registeredFacades { + if m[desc.Name] == nil { + m[desc.Name] = set.NewInts() + } + for _, version := range desc.Versions { + m[desc.Name].Add(version) + } + } + + for name, facadeVersion := range facadeVersions { + c.Logf("checking %q", name) + + // Force the versions to be sorted. + sort.Slice(facadeVersion, func(i, j int) bool { + return facadeVersion[i] < facadeVersion[j] + }) + + versions, ok := m[name] + c.Assert(ok, jc.IsTrue, gc.Commentf("facade %q not registered", name)) + + // Ensure there is a complete overlap. + c.Check(versions.Intersection(set.NewInts(facadeVersion...)).SortedValues(), gc.DeepEquals, []int(facadeVersion), gc.Commentf("facade %q versions not in sync", name)) + } +} diff --git a/apiserver/common/secrets/access.go b/apiserver/common/secrets/access.go index 4e60e887b9b..5b20ae702f1 100644 --- a/apiserver/common/secrets/access.go +++ b/apiserver/common/secrets/access.go @@ -29,11 +29,6 @@ func IsSameApplication(authTag names.Tag, tag names.Tag) bool { return AuthTagApp(authTag) == AuthTagApp(tag) } -func hasRole(api SecretsConsumer, uri *coresecrets.URI, entity names.Tag, role coresecrets.SecretRole) bool { - hasRole, err := api.SecretAccess(uri, entity) - return err == nil && hasRole.Allowed(role) -} - // CanManage checks that the authenticated caller can manage the secret, and returns a // token to ensure leadership if that is required; ie if the request is for a secret // owned by an application, the entity must be the unit leader. @@ -46,16 +41,31 @@ func CanManage( switch authTag.(type) { case names.UnitTag: - if hasRole(api, uri, authTag, coresecrets.RoleManage) { + hasRole, err := api.SecretAccess(uri, authTag) + if err != nil { + // Typically not found error. + return nil, errors.Trace(err) + } + if hasRole.Allowed(coresecrets.RoleManage) { // owner unit. return successfulToken{}, nil } - if hasRole(api, uri, appTag, coresecrets.RoleManage) { + hasRole, err = api.SecretAccess(uri, appTag) + if err != nil { + // Typically not found error. + return nil, errors.Trace(err) + } + if hasRole.Allowed(coresecrets.RoleManage) { // leader unit can manage app owned secret. return LeadershipToken(authTag, leadershipChecker) } case names.ModelTag: - if hasRole(api, uri, authTag, coresecrets.RoleManage) { + hasRole, err := api.SecretAccess(uri, authTag) + if err != nil { + // Typically not found error. + return nil, errors.Trace(err) + } + if hasRole.Allowed(coresecrets.RoleManage) { return successfulToken{}, nil } } @@ -63,17 +73,25 @@ func CanManage( } // CanRead returns true if the specified entity can read the secret. -func CanRead(api SecretsConsumer, authTag names.Tag, uri *coresecrets.URI, entity names.Tag) bool { +func CanRead(api SecretsConsumer, authTag names.Tag, uri *coresecrets.URI, entity names.Tag) (bool, error) { // First try looking up unit access. - hasRole, _ := api.SecretAccess(uri, entity) + hasRole, err := api.SecretAccess(uri, entity) + if err != nil { + // Typically not found error. + return false, errors.Trace(err) + } if hasRole.Allowed(coresecrets.RoleView) { - return true + return true, nil } // All units can read secrets owned by application. appName := AuthTagApp(authTag) - hasRole, _ = api.SecretAccess(uri, names.NewApplicationTag(appName)) - return hasRole.Allowed(coresecrets.RoleView) + hasRole, err = api.SecretAccess(uri, names.NewApplicationTag(appName)) + if err != nil { + // Typically not found error. + return false, errors.Trace(err) + } + return hasRole.Allowed(coresecrets.RoleView), nil } // OwnerToken returns a token used to determine if the specified entity diff --git a/apiserver/common/secrets/access_test.go b/apiserver/common/secrets/access_test.go index d5c5b8db438..c342161f39a 100644 --- a/apiserver/common/secrets/access_test.go +++ b/apiserver/common/secrets/access_test.go @@ -4,7 +4,6 @@ package secrets_test import ( - "github.com/juju/errors" "github.com/juju/names/v4" jc "github.com/juju/testing/checkers" "go.uber.org/mock/gomock" @@ -45,7 +44,7 @@ func (s *secretsSuite) TestCanManageLeaderUnitAppSecret(c *gc.C) { uri := coresecrets.NewURI() gomock.InOrder( - secretsConsumer.EXPECT().SecretAccess(uri, authTag).Return(coresecrets.RoleNone, errors.NotFoundf("")), + secretsConsumer.EXPECT().SecretAccess(uri, authTag).Return(coresecrets.RoleNone, nil), secretsConsumer.EXPECT().SecretAccess(uri, names.NewApplicationTag("mariadb")).Return(coresecrets.RoleManage, nil), leadershipChecker.EXPECT().LeadershipCheck("mariadb", "mariadb/0").Return(token), token.EXPECT().Check().Return(nil), diff --git a/apiserver/facades/agent/secretsmanager/secrets.go b/apiserver/facades/agent/secretsmanager/secrets.go index e298ff2cb25..48757358a06 100644 --- a/apiserver/facades/agent/secretsmanager/secrets.go +++ b/apiserver/facades/agent/secretsmanager/secrets.go @@ -61,7 +61,7 @@ type SecretsManagerAPIV1 struct { *SecretsManagerAPI } -func (s *SecretsManagerAPI) canRead(uri *coresecrets.URI, entity names.Tag) bool { +func (s *SecretsManagerAPI) canRead(uri *coresecrets.URI, entity names.Tag) (bool, error) { return commonsecrets.CanRead(s.secretsConsumer, s.authTag, uri, entity) } @@ -332,10 +332,6 @@ func (s *SecretsManagerAPI) updateSecret(arg params.UpdateSecretArg) error { arg.Label == nil && len(arg.Params) == 0 && len(arg.Content.Data) == 0 && arg.Content.ValueRef == nil { return errors.New("at least one attribute to update must be specified") } - if _, err := s.secretsState.GetSecret(uri); err != nil { - // Check if the uri exists or not. - return errors.Trace(err) - } token, err := s.canManage(uri) if err != nil { @@ -396,8 +392,14 @@ func (s *SecretsManagerAPI) getSecretConsumerInfo(consumerTag names.Tag, uriStr } // We only check read permissions for local secrets. // For CMR secrets, the remote model manages the permissions. - if uri.IsLocal(s.modelUUID) && !s.canRead(uri, consumerTag) { - return nil, apiservererrors.ErrPerm + if uri.IsLocal(s.modelUUID) { + canRead, err := s.canRead(uri, consumerTag) + if err != nil { + return nil, errors.Trace(err) + } + if !canRead { + return nil, apiservererrors.ErrPerm + } } consumer, err := s.secretsConsumer.GetSecretConsumer(uri, consumerTag) if err != nil { @@ -406,7 +408,7 @@ func (s *SecretsManagerAPI) getSecretConsumerInfo(consumerTag names.Tag, uriStr if consumer.Label != "" { return consumer, nil } - md, err := s.getAppOwnedOrUnitOwnedSecretMetadata(uri, "", false, false) + md, err := s.getAppOwnedOrUnitOwnedSecretMetadata(uri, "") if errors.Is(err, errors.NotFound) { // The secret is owned by a different application. return consumer, nil @@ -585,74 +587,43 @@ func (s *SecretsManagerAPI) GetSecretRevisionContentInfo(ctx context.Context, ar return result, nil } -// For the application owned secret, if the caller is a peer unit, we create a fake consumer doc for triggering events to notify the uniters. -// The peer units should get the secret using owner label but should not set a consumer label. -func (s *SecretsManagerAPI) ensureConsumerMetadataForAppOwnedSecretsForPeerUnits(md *coresecrets.SecretMetadata) (*coresecrets.SecretMetadata, error) { - consumer, err := s.secretsConsumer.GetSecretConsumer(md.URI, s.authTag) - if err != nil && !errors.Is(err, errors.NotFound) { - return nil, errors.Trace(err) - } - - if consumer == nil { - // Create a fake consumer doc for triggering secret-changed event for uniter. - consumer = &coresecrets.SecretConsumerMetadata{} - } - s.logger.Debugf("saving consumer doc for application owned secret %q for peer units %q", md.URI, s.authTag) - if err := s.secretsConsumer.SaveSecretConsumer(md.URI, s.authTag, consumer); err != nil { - return nil, errors.Trace(err) - } - return md, nil -} - -func (s *SecretsManagerAPI) updateLabelForAppOwnedOrUnitOwnedSecret(uri *coresecrets.URI, label string, md *coresecrets.SecretMetadata) error { +func (s *SecretsManagerAPI) updateLabelForAppOwnedOrUnitOwnedSecret(uri *coresecrets.URI, label string, owner string) error { if uri == nil || label == "" { // We have done this check before, but it doesn't hurt to do it again. return nil } - ownerTag, err := names.ParseTag(md.OwnerTag) - if err != nil { - return errors.Trace(err) - } - isLeaderUnit, err := commonsecrets.IsLeaderUnit(s.authTag, s.leadershipChecker) + ownerTag, err := names.ParseTag(owner) if err != nil { return errors.Trace(err) } - if ownerTag == s.authTag || commonsecrets.IsSameApplication(ownerTag, s.authTag) && isLeaderUnit { - // The secret is owned by the caller or the caller is the leader unit of the application owning the secret. - token, err := commonsecrets.LeadershipToken(s.authTag, s.leadershipChecker) + if ownerTag != s.authTag { + isLeaderUnit, err := commonsecrets.IsLeaderUnit(s.authTag, s.leadershipChecker) if err != nil { return errors.Trace(err) } - // Update the label. - _, err = s.secretsState.UpdateSecret(uri, state.UpdateSecretParams{ - LeaderToken: token, - Label: &label, - }) + if !isLeaderUnit { + return errors.New("only unit leaders can update an application owned secret label") + } + } + + token, err := commonsecrets.OwnerToken(s.authTag, ownerTag, s.leadershipChecker) + if err != nil { return errors.Trace(err) } - return nil + // Update the label. + _, err = s.secretsState.UpdateSecret(uri, state.UpdateSecretParams{ + LeaderToken: token, + Label: &label, + }) + return errors.Trace(err) } -func (s *SecretsManagerAPI) getAppOwnedOrUnitOwnedSecretMetadata(uri *coresecrets.URI, label string, ensureConsumerMetaData, updateLabel bool) (md *coresecrets.SecretMetadata, err error) { +func (s *SecretsManagerAPI) getAppOwnedOrUnitOwnedSecretMetadata(uri *coresecrets.URI, label string) (*coresecrets.SecretMetadata, error) { notFoundErr := errors.NotFoundf("secret %q", uri) if label != "" { notFoundErr = errors.NotFoundf("secret with label %q", label) } - defer func() { - if md == nil { - return - } - if updateLabel { - if err = s.updateLabelForAppOwnedOrUnitOwnedSecret(uri, label, md); err != nil { - return - } - } - if md.OwnerTag == s.authTag.String() || !ensureConsumerMetaData { - return - } - md, err = s.ensureConsumerMetadataForAppOwnedSecretsForPeerUnits(md) - }() filter := state.SecretsFilter{ OwnerTags: []names.Tag{s.authTag}, @@ -701,27 +672,29 @@ func (s *SecretsManagerAPI) getSecretContent(arg params.GetSecretContentArg) ( // arg.Label could be the consumer label for consumers or the owner label for owners. possibleUpdateLabel := arg.Label != "" && uri != nil + labelToUpdate := arg.Label // For local secrets, check those which may be owned by the caller. if uri == nil || uri.IsLocal(s.modelUUID) { - // Owner units should always have the URI because we resolved the label to URI on uniter side already. - md, err := s.getAppOwnedOrUnitOwnedSecretMetadata(uri, arg.Label, true, possibleUpdateLabel) + md, err := s.getAppOwnedOrUnitOwnedSecretMetadata(uri, arg.Label) if err != nil && !errors.Is(err, errors.NotFound) { return nil, nil, false, errors.Trace(err) } if md != nil { + // If the label has is to be changed by the secret owner, update the secret metadata. + // TODO(wallyworld) - the label staying the same should be asserted in a txn. + possibleUpdateLabel = possibleUpdateLabel && labelToUpdate != md.Label + if possibleUpdateLabel { + if err = s.updateLabelForAppOwnedOrUnitOwnedSecret(uri, labelToUpdate, md.OwnerTag); err != nil { + return nil, nil, false, errors.Trace(err) + } + } // 1. secrets can be accessed by the owner; // 2. application owned secrets can be accessed by all the units of the application using owner label or URI. - val, valueRef, err := s.secretsState.GetSecretValue(md.URI, md.LatestRevision) - if err != nil { - return nil, nil, false, errors.Trace(err) - } - content := &secrets.ContentParams{SecretValue: val, ValueRef: valueRef} - if err != nil || content.ValueRef == nil { - return content, nil, false, errors.Trace(err) - } - backend, draining, err := s.getBackend(content.ValueRef.BackendID) - return content, backend, draining, errors.Trace(err) + uri = md.URI + // We don't update the consumer label in this case since the label comes + // from the owner metadata and we don't want to violate uniqueness checks. + labelToUpdate = "" } } @@ -741,16 +714,16 @@ func (s *SecretsManagerAPI) getSecretContent(arg params.GetSecretContentArg) ( return s.getRemoteSecretContent(uri, arg.Refresh, arg.Peek, arg.Label, possibleUpdateLabel) } - if _, err := s.secretsState.GetSecret(uri); err != nil { - // Check if the uri exists or not. + canRead, err := s.canRead(uri, s.authTag) + if err != nil { return nil, nil, false, errors.Trace(err) } - if !s.canRead(uri, s.authTag) { + if !canRead { return nil, nil, false, apiservererrors.ErrPerm } - // arg.Label is the consumer label for consumers. - consumedRevision, err := s.getConsumedRevision(uri, arg.Refresh, arg.Peek, arg.Label, possibleUpdateLabel) + // labelToUpdate is the consumer label for consumers. + consumedRevision, err := s.getConsumedRevision(uri, arg.Refresh, arg.Peek, labelToUpdate, possibleUpdateLabel) if err != nil { return nil, nil, false, errors.Annotate(err, "getting latest secret revision") } @@ -764,6 +737,24 @@ func (s *SecretsManagerAPI) getSecretContent(arg params.GetSecretContentArg) ( return content, backend, draining, errors.Trace(err) } +// UpdateTrackedRevisions updates the consumer info to track the latest +// revisions for the specified secrets. +func (s *SecretsManagerAPI) UpdateTrackedRevisions(uris []string) (params.ErrorResults, error) { + result := params.ErrorResults{ + Results: make([]params.ErrorResult, len(uris)), + } + for i, uriStr := range uris { + uri, err := coresecrets.ParseURI(uriStr) + if err != nil { + result.Results[i].Error = apiservererrors.ServerError(err) + continue + } + _, err = s.getConsumedRevision(uri, true, false, "", false) + result.Results[i].Error = apiservererrors.ServerError(err) + } + return result, nil +} + func (s *SecretsManagerAPI) getConsumedRevision(uri *coresecrets.URI, refresh, peek bool, label string, possibleUpdateLabel bool) (int, error) { consumerInfo, err := s.secretsConsumer.GetSecretConsumer(uri, s.authTag) if err != nil && !errors.Is(err, errors.NotFound) { diff --git a/apiserver/facades/agent/secretsmanager/secrets_test.go b/apiserver/facades/agent/secretsmanager/secrets_test.go index e234fac7dd5..5dcd5e290e9 100644 --- a/apiserver/facades/agent/secretsmanager/secrets_test.go +++ b/apiserver/facades/agent/secretsmanager/secrets_test.go @@ -22,6 +22,7 @@ import ( facademocks "github.com/juju/juju/apiserver/facade/mocks" "github.com/juju/juju/apiserver/facades/agent/secretsmanager" "github.com/juju/juju/apiserver/facades/agent/secretsmanager/mocks" + "github.com/juju/juju/core/leadership" coresecrets "github.com/juju/juju/core/secrets" corewatcher "github.com/juju/juju/core/watcher" "github.com/juju/juju/internal/secrets" @@ -388,7 +389,6 @@ func (s *SecretsManagerSuite) TestUpdateSecrets(c *gc.C) { ) s.leadership.EXPECT().LeadershipCheck("mariadb", "mariadb/0").Return(s.token).Times(2) s.token.EXPECT().Check().Return(nil).Times(2) - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil).Times(2) s.expectSecretAccessQuery(4) results, err := s.facade.UpdateSecrets(context.Background(), params.UpdateSecretArgs{ @@ -436,7 +436,6 @@ func (s *SecretsManagerSuite) TestUpdateSecretDuplicateLabel(c *gc.C) { } uri := coresecrets.NewURI() expectURI := *uri - s.secretsState.EXPECT().GetSecret(&expectURI).Return(&coresecrets.SecretMetadata{}, nil) s.secretsState.EXPECT().UpdateSecret(&expectURI, p).Return( nil, fmt.Errorf("dup label %w", state.LabelExists), ) @@ -711,18 +710,27 @@ func (s *SecretsManagerSuite) TestGetSecretContentForOwnerSecretURIArg(c *gc.C) data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() + md := coresecrets.SecretMetadata{ + URI: uri, + LatestRevision: 668, + OwnerTag: s.authTag.String(), + } + + s.expectSecretAccessQuery(1) + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ OwnerTags: []names.Tag{ names.NewUnitTag("mariadb/0"), names.NewApplicationTag("mariadb"), }, - }).Return([]*coresecrets.SecretMetadata{ - { - URI: uri, - LatestRevision: 668, - OwnerTag: s.authTag.String(), - }, - }, nil) + }).Return([]*coresecrets.SecretMetadata{&md}, nil) + + s.secretsState.EXPECT().GetSecret(uri).Return(&md, nil) + s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). + Return(nil, errors.NotFoundf("secret consumer")) + s.secretsConsumer.EXPECT().SaveSecretConsumer( + uri, s.authTag, &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) + s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( val, nil, nil, ) @@ -746,19 +754,28 @@ func (s *SecretsManagerSuite) TestGetSecretContentForOwnerSecretLabelArg(c *gc.C data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() + md := coresecrets.SecretMetadata{ + URI: uri, + LatestRevision: 668, + Label: "foo", + OwnerTag: s.authTag.String(), + } + + s.expectSecretAccessQuery(1) + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ OwnerTags: []names.Tag{ names.NewUnitTag("mariadb/0"), names.NewApplicationTag("mariadb"), }, - }).Return([]*coresecrets.SecretMetadata{ - { - URI: uri, - LatestRevision: 668, - Label: "foo", - OwnerTag: s.authTag.String(), - }, - }, nil) + }).Return([]*coresecrets.SecretMetadata{&md}, nil) + + s.secretsState.EXPECT().GetSecret(uri).Return(&md, nil) + s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). + Return(nil, errors.NotFoundf("secret consumer")) + s.secretsConsumer.EXPECT().SaveSecretConsumer( + uri, s.authTag, &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) + s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( val, nil, nil, ) @@ -782,25 +799,38 @@ func (s *SecretsManagerSuite) TestGetSecretContentForUnitOwnedSecretUpdateLabel( data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() + md := coresecrets.SecretMetadata{ + URI: uri, + LatestRevision: 668, + Label: "foz", + OwnerTag: s.authTag.String(), + } + + s.expectSecretAccessQuery(1) + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ OwnerTags: []names.Tag{ names.NewUnitTag("mariadb/0"), names.NewApplicationTag("mariadb"), }, - }).Return([]*coresecrets.SecretMetadata{ - { - URI: uri, - LatestRevision: 668, - Label: "foo", - OwnerTag: s.authTag.String(), + }).Return([]*coresecrets.SecretMetadata{&md}, nil) + + // Label is updated on owner metadata, not consumer metadata since it is a secret owned by the caller. + s.secretsState.EXPECT().UpdateSecret(uri, gomock.Any()).DoAndReturn( + func(uri *coresecrets.URI, p state.UpdateSecretParams) (*coresecrets.SecretMetadata, error) { + c.Assert(p.LeaderToken, gc.NotNil) + c.Assert(p.LeaderToken.Check(), jc.ErrorIsNil) + c.Assert(p.Label, gc.NotNil) + c.Assert(*p.Label, gc.Equals, "foo") + return nil, nil }, - }, nil) - s.leadership.EXPECT().LeadershipCheck("mariadb", "mariadb/0").Return(s.token).Times(2) - s.token.EXPECT().Check().Return(nil).Times(2) - s.secretsState.EXPECT().UpdateSecret(uri, state.UpdateSecretParams{ - LeaderToken: s.token, - Label: ptr("foo"), - }).Return(nil, nil) + ) + + s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). + Return(nil, errors.NotFoundf("secret consumer")) + s.secretsState.EXPECT().GetSecret(uri).Return(&md, nil) + s.secretsConsumer.EXPECT().SaveSecretConsumer( + uri, s.authTag, &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( val, nil, nil, @@ -825,6 +855,9 @@ func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretUpdateLabel(c *gc. data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() + + s.expectSecretAccessQuery(1) + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ OwnerTags: []names.Tag{ names.NewUnitTag("mariadb/0"), @@ -834,7 +867,7 @@ func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretUpdateLabel(c *gc. { URI: uri, LatestRevision: 668, - Label: "foo", + Label: "foz", OwnerTag: names.NewApplicationTag("mariadb").String(), }, }, nil) @@ -847,9 +880,9 @@ func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretUpdateLabel(c *gc. s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). Return(nil, errors.NotFoundf("secret consumer")) + s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{LatestRevision: 668}, nil) s.secretsConsumer.EXPECT().SaveSecretConsumer( - uri, names.NewUnitTag("mariadb/0"), &coresecrets.SecretConsumerMetadata{}).Return(nil) - + uri, names.NewUnitTag("mariadb/0"), &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( val, nil, nil, ) @@ -867,12 +900,49 @@ func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretUpdateLabel(c *gc. }) } -func (s *SecretsManagerSuite) TestGetSecretContentForUnitAccessApplicationOwnedSecret(c *gc.C) { +func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretUpdateLabelNotLeader(c *gc.C) { + defer s.setup(c).Finish() + + uri := coresecrets.NewURI() + + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ + OwnerTags: []names.Tag{ + names.NewUnitTag("mariadb/0"), + names.NewApplicationTag("mariadb"), + }, + }).Return([]*coresecrets.SecretMetadata{ + { + URI: uri, + LatestRevision: 668, + Label: "foz", + OwnerTag: names.NewApplicationTag("mariadb").String(), + }, + }, nil) + s.leadership.EXPECT().LeadershipCheck("mariadb", "mariadb/0").Return(s.token) + s.token.EXPECT().Check().Return(leadership.NewNotLeaderError("mariadb/0", "mariadb")) + + results, err := s.facade.GetSecretContentInfo(context.Background(), params.GetSecretContentArgs{ + Args: []params.GetSecretContentArg{ + {URI: uri.String(), Label: "foo"}, + }, + }) + c.Assert(err, jc.ErrorIsNil) + c.Assert(results, jc.DeepEquals, params.SecretContentResults{ + Results: []params.SecretContentResult{{ + Error: ¶ms.Error{Message: "only unit leaders can update an application owned secret label"}, + }}, + }) +} + +func (s *SecretsManagerSuite) TestGetSecretContentForAppSecretSameLabel(c *gc.C) { defer s.setup(c).Finish() data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() + + s.expectSecretAccessQuery(1) + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ OwnerTags: []names.Tag{ names.NewUnitTag("mariadb/0"), @@ -889,8 +959,53 @@ func (s *SecretsManagerSuite) TestGetSecretContentForUnitAccessApplicationOwnedS s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). Return(nil, errors.NotFoundf("secret consumer")) + s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{LatestRevision: 668}, nil) s.secretsConsumer.EXPECT().SaveSecretConsumer( - uri, names.NewUnitTag("mariadb/0"), &coresecrets.SecretConsumerMetadata{}).Return(nil) + uri, names.NewUnitTag("mariadb/0"), &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) + s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( + val, nil, nil, + ) + + results, err := s.facade.GetSecretContentInfo(context.Background(), params.GetSecretContentArgs{ + Args: []params.GetSecretContentArg{ + {URI: uri.String(), Label: "foo"}, + }, + }) + c.Assert(err, jc.ErrorIsNil) + c.Assert(results, jc.DeepEquals, params.SecretContentResults{ + Results: []params.SecretContentResult{{ + Content: params.SecretContentParams{Data: data}, + }}, + }) +} + +func (s *SecretsManagerSuite) TestGetSecretContentForUnitAccessApplicationOwnedSecret(c *gc.C) { + defer s.setup(c).Finish() + + data := map[string]string{"foo": "bar"} + val := coresecrets.NewSecretValue(data) + uri := coresecrets.NewURI() + md := coresecrets.SecretMetadata{ + URI: uri, + LatestRevision: 668, + Label: "foo", + OwnerTag: names.NewApplicationTag("mariadb").String(), + } + + s.expectSecretAccessQuery(1) + + s.secretsState.EXPECT().ListSecrets(state.SecretsFilter{ + OwnerTags: []names.Tag{ + names.NewUnitTag("mariadb/0"), + names.NewApplicationTag("mariadb"), + }, + }).Return([]*coresecrets.SecretMetadata{&md}, nil) + + s.secretsState.EXPECT().GetSecret(uri).Return(&md, nil) + s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). + Return(nil, errors.NotFoundf("secret consumer")) + s.secretsConsumer.EXPECT().SaveSecretConsumer( + uri, names.NewUnitTag("mariadb/0"), &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) s.secretsState.EXPECT().GetSecretValue(uri, 668).Return( val, nil, nil, @@ -929,7 +1044,6 @@ func (s *SecretsManagerSuite) assertGetSecretContentConsumer(c *gc.C, isUnitAgen data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.secretsState.EXPECT().ListSecrets(filter).Return([]*coresecrets.SecretMetadata{}, nil) @@ -969,7 +1083,6 @@ func (s *SecretsManagerSuite) TestGetSecretContentConsumerLabelOnly(c *gc.C) { data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.secretsConsumer.EXPECT().GetURIByConsumerLabel("label", names.NewUnitTag("mariadb/0")).Return(uri, nil) @@ -1007,7 +1120,6 @@ func (s *SecretsManagerSuite) TestGetSecretContentConsumerFirstTime(c *gc.C) { data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.expectgetAppOwnedOrUnitOwnedSecretMetadataNotFound() @@ -1044,7 +1156,6 @@ func (s *SecretsManagerSuite) TestGetSecretContentConsumerUpdateLabel(c *gc.C) { data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.expectgetAppOwnedOrUnitOwnedSecretMetadataNotFound() @@ -1100,7 +1211,6 @@ func (s *SecretsManagerSuite) TestGetSecretContentConsumerUpdateArg(c *gc.C) { data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.expectgetAppOwnedOrUnitOwnedSecretMetadataNotFound() @@ -1138,7 +1248,6 @@ func (s *SecretsManagerSuite) TestGetSecretContentConsumerPeekArg(c *gc.C) { data := map[string]string{"foo": "bar"} val := coresecrets.NewSecretValue(data) uri := coresecrets.NewURI() - s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{}, nil) s.expectSecretAccessQuery(1) s.expectgetAppOwnedOrUnitOwnedSecretMetadataNotFound() @@ -1815,3 +1924,20 @@ func (s *SecretsManagerSuite) TestSecretsRevoke(c *gc.C) { }, }) } + +func (s *SecretsManagerSuite) TestUpdateTrackedRevisions(c *gc.C) { + defer s.setup(c).Finish() + + uri := coresecrets.NewURI() + s.secretsState.EXPECT().GetSecret(uri).Return(&coresecrets.SecretMetadata{ + LatestRevision: 668, + }, nil).AnyTimes() + s.secretsConsumer.EXPECT().GetSecretConsumer(uri, s.authTag). + Return(nil, errors.NotFoundf("secret consumer")) + s.secretsConsumer.EXPECT().SaveSecretConsumer( + uri, s.authTag, &coresecrets.SecretConsumerMetadata{LatestRevision: 668, CurrentRevision: 668}).Return(nil) + + result, err := s.facade.UpdateTrackedRevisions([]string{uri.ID}) + c.Assert(err, jc.ErrorIsNil) + c.Assert(result, jc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{}}}) +} diff --git a/apiserver/facades/agent/uniter/uniter.go b/apiserver/facades/agent/uniter/uniter.go index a4f43e699c9..25645da19f5 100644 --- a/apiserver/facades/agent/uniter/uniter.go +++ b/apiserver/facades/agent/uniter/uniter.go @@ -2595,15 +2595,6 @@ func (u *UniterAPI) commitHookChangesForOneUnit(ctx context.Context, unitTag nam } // TODO - do in txn once we have support for that - if len(changes.SecretDeletes) > 0 { - result, err := u.SecretsManagerAPI.RemoveSecrets(context.Background(), params.DeleteSecretArgs{Args: changes.SecretDeletes}) - if err == nil { - err = result.Combine() - } - if err != nil { - return errors.Annotate(err, "removing secrets") - } - } if len(changes.SecretCreates) > 0 { result, err := u.SecretsManagerAPI.CreateSecrets(context.Background(), params.CreateSecretArgs{Args: changes.SecretCreates}) if err == nil { @@ -2630,6 +2621,15 @@ func (u *UniterAPI) commitHookChangesForOneUnit(ctx context.Context, unitTag nam return errors.Annotate(err, "updating secrets") } } + if len(changes.TrackLatest) > 0 { + result, err := u.SecretsManagerAPI.UpdateTrackedRevisions(changes.TrackLatest) + if err == nil { + err = result.Combine() + } + if err != nil { + return errors.Annotate(err, "updating secret tracked revisions") + } + } if len(changes.SecretGrants) > 0 { result, err := u.SecretsManagerAPI.SecretsGrant(context.Background(), params.GrantRevokeSecretArgs{Args: changes.SecretGrants}) if err == nil { @@ -2648,6 +2648,15 @@ func (u *UniterAPI) commitHookChangesForOneUnit(ctx context.Context, unitTag nam return errors.Annotate(err, "revoking secrets access") } } + if len(changes.SecretDeletes) > 0 { + result, err := u.SecretsManagerAPI.RemoveSecrets(context.Background(), params.DeleteSecretArgs{Args: changes.SecretDeletes}) + if err == nil { + err = result.Combine() + } + if err != nil { + return errors.Annotate(err, "removing secrets") + } + } // Apply all changes in a single transaction. return u.st.ApplyOperation(state.ComposeModelOperations(modelOps...)) diff --git a/apiserver/facades/agent/uniter/uniter_test.go b/apiserver/facades/agent/uniter/uniter_test.go index f14a2d4c813..6a5d3a70fe4 100644 --- a/apiserver/facades/agent/uniter/uniter_test.go +++ b/apiserver/facades/agent/uniter/uniter_test.go @@ -3496,7 +3496,11 @@ func (s *uniterSuite) TestCommitHookChangesWithSecrets(c *gc.C) { Description: ptr("a secret"), Label: ptr("foobar"), Value: secrets.NewSecretValue(map[string]string{"foo": "bar2"}), + }, { + URI: uri3, + Value: secrets.NewSecretValue(map[string]string{"foo3": "bar3"}), }}) + b.AddTrackLatest([]string{uri3.ID}) b.AddSecretDeletes([]apiuniter.SecretDeleteArg{{URI: uri3, Revision: ptr(1)}}) b.AddSecretGrants([]apiuniter.SecretGrantRevokeArgs{{ URI: uri, @@ -3522,7 +3526,7 @@ func (s *uniterSuite) TestCommitHookChangesWithSecrets(c *gc.C) { }) // Verify state - _, err = store.GetSecret(uri3) + _, _, err = store.GetSecretValue(uri3, 1) c.Assert(err, jc.ErrorIs, errors.NotFound) md, err := store.GetSecret(uri) c.Assert(err, jc.ErrorIsNil) @@ -3538,6 +3542,11 @@ func (s *uniterSuite) TestCommitHookChangesWithSecrets(c *gc.C) { access, err = st.SecretAccess(uri2, s.mysql.Tag()) c.Assert(err, jc.ErrorIsNil) c.Assert(access, gc.Equals, secrets.RoleNone) + + info, err := st.GetSecretConsumer(uri3, s.wordpressUnit.Tag()) + c.Assert(err, jc.ErrorIsNil) + c.Assert(info.CurrentRevision, gc.Equals, 2) + c.Assert(info.LatestRevision, gc.Equals, 2) } func (s *uniterSuite) TestCommitHookChangesWithStorage(c *gc.C) { diff --git a/apiserver/facades/controller/migrationtarget/migrationtarget.go b/apiserver/facades/controller/migrationtarget/migrationtarget.go index 35150385425..e9a820012f9 100644 --- a/apiserver/facades/controller/migrationtarget/migrationtarget.go +++ b/apiserver/facades/controller/migrationtarget/migrationtarget.go @@ -5,6 +5,7 @@ package migrationtarget import ( "context" + "fmt" "time" "github.com/juju/errors" @@ -18,6 +19,7 @@ import ( "github.com/juju/juju/controller" "github.com/juju/juju/core/changestream" "github.com/juju/juju/core/crossmodel" + "github.com/juju/juju/core/facades" coremigration "github.com/juju/juju/core/migration" "github.com/juju/juju/core/modelmigration" "github.com/juju/juju/core/permission" @@ -65,20 +67,21 @@ type UpgradeService interface { // API implements the API required for the model migration // master worker when communicating with the target controller. type API struct { - state *state.State - modelImporter ModelImporter - externalControllerService ExternalControllerService - cloudService common.CloudService - upgradeService UpgradeService - credentialService credentialcommon.CredentialService - credentialValidator credentialservice.CredentialValidator - credentialCallContextGetter credentialservice.ValidationContextGetter - credentialInvalidatorGetter environscontext.ModelCredentialInvalidatorGetter - pool *state.StatePool - authorizer facade.Authorizer - presence facade.Presence - getEnviron stateenvirons.NewEnvironFunc - getCAASBroker stateenvirons.NewCAASBrokerFunc + state *state.State + modelImporter ModelImporter + externalControllerService ExternalControllerService + cloudService common.CloudService + upgradeService UpgradeService + credentialService credentialcommon.CredentialService + credentialValidator credentialservice.CredentialValidator + credentialCallContextGetter credentialservice.ValidationContextGetter + credentialInvalidatorGetter environscontext.ModelCredentialInvalidatorGetter + pool *state.StatePool + authorizer facade.Authorizer + presence facade.Presence + getEnviron stateenvirons.NewEnvironFunc + getCAASBroker stateenvirons.NewCAASBrokerFunc + requiredMigrationFacadeVersions facades.FacadeVersions } // APIV1 implements the V1 version of the API facade. @@ -86,6 +89,11 @@ type APIV1 struct { *API } +// APIV2 implements the V2 version of the API facade. +type APIV2 struct { + *APIV1 +} + // NewAPI returns a new APIV1. Accepts a NewEnvironFunc and envcontext.ProviderCallContext // for testing purposes. func NewAPI( @@ -101,6 +109,7 @@ func NewAPI( credentialInvalidatorGetter environscontext.ModelCredentialInvalidatorGetter, getEnviron stateenvirons.NewEnvironFunc, getCAASBroker stateenvirons.NewCAASBrokerFunc, + requiredMigrationFacadeVersions facades.FacadeVersions, ) (*API, error) { var ( st = ctx.State() @@ -112,20 +121,21 @@ func NewAPI( ) return &API{ - state: st, - modelImporter: modelImporter, - pool: pool, - externalControllerService: externalControllerService, - cloudService: cloudService, - upgradeService: upgradeService, - credentialService: credentialService, - credentialValidator: validator, - credentialCallContextGetter: credentialCallContextGetter, - credentialInvalidatorGetter: credentialInvalidatorGetter, - authorizer: authorizer, - presence: ctx.Presence(), - getEnviron: getEnviron, - getCAASBroker: getCAASBroker, + state: st, + modelImporter: modelImporter, + pool: pool, + externalControllerService: externalControllerService, + cloudService: cloudService, + upgradeService: upgradeService, + credentialService: credentialService, + credentialValidator: validator, + credentialCallContextGetter: credentialCallContextGetter, + credentialInvalidatorGetter: credentialInvalidatorGetter, + authorizer: authorizer, + presence: ctx.Presence(), + getEnviron: getEnviron, + getCAASBroker: getCAASBroker, + requiredMigrationFacadeVersions: requiredMigrationFacadeVersions, }, nil } @@ -140,6 +150,36 @@ func checkAuth(authorizer facade.Authorizer, st *state.State) error { // Prechecks ensure that the target controller is ready to accept a // model migration. func (api *API) Prechecks(ctx context.Context, model params.MigrationModelInfo) error { + // If there are no required migration facade versions, then we + // don't need to check anything. + if len(api.requiredMigrationFacadeVersions) > 0 { + // Ensure that when attempting to migrate a model, the source + // controller has the required facades for the migration. + sourceFacadeVersions := facades.FacadeVersions{} + for name, versions := range model.FacadeVersions { + sourceFacadeVersions[name] = versions + } + if !facades.CompleteIntersection(api.requiredMigrationFacadeVersions, sourceFacadeVersions) { + majorMinor := fmt.Sprintf("%d.%d", + model.ControllerAgentVersion.Major, + model.ControllerAgentVersion.Minor, + ) + + // If the patch is zero, then we don't need to mention it. + var patchMessage string + if model.ControllerAgentVersion.Patch > 0 { + patchMessage = fmt.Sprintf(", that is greater than %s.%d", majorMinor, model.ControllerAgentVersion.Patch) + } + + return errors.Errorf(` +Source controller does not support required facades for performing migration. +Upgrade the controller to a newer version of %s%s or migrate to a controller +with an earlier version of the target controller and try again. + +`[1:], majorMinor, patchMessage) + } + } + ownerTag, err := names.ParseUserTag(model.OwnerTag) if err != nil { return errors.Trace(err) diff --git a/apiserver/facades/controller/migrationtarget/migrationtarget_test.go b/apiserver/facades/controller/migrationtarget/migrationtarget_test.go index 9d857e7552f..363eada43c7 100644 --- a/apiserver/facades/controller/migrationtarget/migrationtarget_test.go +++ b/apiserver/facades/controller/migrationtarget/migrationtarget_test.go @@ -27,6 +27,7 @@ import ( "github.com/juju/juju/caas" "github.com/juju/juju/cloud" "github.com/juju/juju/core/crossmodel" + "github.com/juju/juju/core/facades" "github.com/juju/juju/core/instance" "github.com/juju/juju/domain/credential" "github.com/juju/juju/domain/credential/service" @@ -76,7 +77,7 @@ func (s *Suite) SetUpTest(c *gc.C) { func (s *Suite) TestFacadeRegistered(c *gc.C) { defer s.setupMocks(c).Finish() - aFactory, err := apiserver.AllFacades().GetFactory("MigrationTarget", 2) + aFactory, err := apiserver.AllFacades().GetFactory("MigrationTarget", 3) c.Assert(err, jc.ErrorIsNil) api, err := aFactory(&facadetest.Context{ @@ -88,6 +89,21 @@ func (s *Suite) TestFacadeRegistered(c *gc.C) { c.Assert(api, gc.FitsTypeOf, new(migrationtarget.API)) } +func (s *Suite) TestFacadeRegisteredV2(c *gc.C) { + defer s.setupMocks(c).Finish() + + aFactory, err := apiserver.AllFacades().GetFactory("MigrationTarget", 2) + c.Assert(err, jc.ErrorIsNil) + + api, err := aFactory(&facadetest.Context{ + State_: s.State, + Auth_: s.authorizer, + ServiceFactory_: servicefactorytesting.NewTestingServiceFactory(), + }) + c.Assert(err, jc.ErrorIsNil) + c.Assert(api, gc.FitsTypeOf, new(migrationtarget.APIV2)) +} + func (s *Suite) importModel(c *gc.C, api *migrationtarget.API) names.ModelTag { uuid, bytes := s.makeExportedModel(c) err := api.Import(context.Background(), params.SerializedModel{Bytes: bytes}) @@ -155,6 +171,45 @@ func (s *Suite) TestPrechecksFail(c *gc.C) { c.Assert(err, gc.NotNil) } +func (s *Suite) TestPrechecksFacadeVersionsFail(c *gc.C) { + controllerVersion := s.controllerVersion(c) + + api := s.mustNewAPIWithFacadeVersions(c, facades.FacadeVersions{ + "MigrationTarget": []int{1}, + }) + args := params.MigrationModelInfo{ + AgentVersion: controllerVersion, + ControllerAgentVersion: controllerVersion, + } + err := api.Prechecks(context.Background(), args) + c.Assert(err, gc.ErrorMatches, ` +Source controller does not support required facades for performing migration. +Upgrade the controller to a newer version of .* or migrate to a controller +with an earlier version of the target controller and try again. + +`[1:]) +} + +func (s *Suite) TestPrechecksFacadeVersionsWithPatchFail(c *gc.C) { + controllerVersion := s.controllerVersion(c) + controllerVersion.Patch++ + + api := s.mustNewAPIWithFacadeVersions(c, facades.FacadeVersions{ + "MigrationTarget": []int{1}, + }) + args := params.MigrationModelInfo{ + AgentVersion: controllerVersion, + ControllerAgentVersion: controllerVersion, + } + err := api.Prechecks(context.Background(), args) + c.Assert(err, gc.ErrorMatches, ` +Source controller does not support required facades for performing migration. +Upgrade the controller to a newer version of .* or migrate to a controller +with an earlier version of the target controller and try again. + +`[1:]) +} + func (s *Suite) TestImport(c *gc.C) { defer s.setupMocks(c).Finish() @@ -336,7 +391,7 @@ func (s *Suite) TestAdoptIAASResources(c *gc.C) { return &env, nil }, func(model stateenvirons.Model, _ stateenvirons.CloudService, _ stateenvirons.CredentialService) (caas.Broker, error) { return nil, errors.New("should not be called") - }) + }, facades.FacadeVersions{}) c.Assert(err, jc.ErrorIsNil) m, err := st.Model() @@ -367,7 +422,7 @@ func (s *Suite) TestAdoptCAASResources(c *gc.C) { }, func(model stateenvirons.Model, _ stateenvirons.CloudService, _ stateenvirons.CredentialService) (caas.Broker, error) { c.Assert(model.ModelTag().Id(), gc.Equals, st.ModelUUID()) return &broker, nil - }) + }, facades.FacadeVersions{}) c.Assert(err, jc.ErrorIsNil) m, err := st.Model() @@ -555,7 +610,7 @@ func (s *Suite) setupMocks(c *gc.C) *gomock.Controller { return ctrl } -func (s *Suite) newAPI(environFunc stateenvirons.NewEnvironFunc, brokerFunc stateenvirons.NewCAASBrokerFunc) (*migrationtarget.API, error) { +func (s *Suite) newAPI(environFunc stateenvirons.NewEnvironFunc, brokerFunc stateenvirons.NewCAASBrokerFunc, versions facades.FacadeVersions) (*migrationtarget.API, error) { return migrationtarget.NewAPI( &s.facadeContext, s.authorizer, @@ -575,11 +630,23 @@ func (s *Suite) newAPI(environFunc stateenvirons.NewEnvironFunc, brokerFunc stat }, environFunc, brokerFunc, + versions, ) } func (s *Suite) mustNewAPI(c *gc.C) *migrationtarget.API { - api, err := s.newAPI(nil, nil) + api, err := s.newAPI(nil, nil, facades.FacadeVersions{}) + c.Assert(err, jc.ErrorIsNil) + return api +} + +func (s *Suite) newAPIWithFacadeVersions(environFunc stateenvirons.NewEnvironFunc, brokerFunc stateenvirons.NewCAASBrokerFunc, versions facades.FacadeVersions) (*migrationtarget.API, error) { + api, err := s.newAPI(environFunc, brokerFunc, versions) + return api, err +} + +func (s *Suite) mustNewAPIWithFacadeVersions(c *gc.C, versions facades.FacadeVersions) *migrationtarget.API { + api, err := s.newAPIWithFacadeVersions(nil, nil, versions) c.Assert(err, jc.ErrorIsNil) return api } @@ -589,7 +656,7 @@ func (s *Suite) mustNewAPIWithModel(c *gc.C, env environs.Environ, broker caas.B return env, nil }, func(stateenvirons.Model, stateenvirons.CloudService, stateenvirons.CredentialService) (caas.Broker, error) { return broker, nil - }) + }, facades.FacadeVersions{}) c.Assert(err, jc.ErrorIsNil) return api } diff --git a/apiserver/facades/controller/migrationtarget/register.go b/apiserver/facades/controller/migrationtarget/register.go index 45a89e89cdf..45d7cf6d6ff 100644 --- a/apiserver/facades/controller/migrationtarget/register.go +++ b/apiserver/facades/controller/migrationtarget/register.go @@ -12,6 +12,7 @@ import ( "github.com/juju/juju/apiserver/common/credentialcommon" "github.com/juju/juju/apiserver/facade" "github.com/juju/juju/caas" + "github.com/juju/juju/core/facades" "github.com/juju/juju/domain/credential/service" "github.com/juju/juju/domain/model" "github.com/juju/juju/environs" @@ -19,26 +20,40 @@ import ( ) // Register is called to expose a package of facades onto a given registry. -func Register(registry facade.FacadeRegistry) { - registry.MustRegister("MigrationTarget", 1, func(ctx facade.Context) (facade.Facade, error) { - return newFacadeV1(ctx) - }, reflect.TypeOf((*APIV1)(nil))) - registry.MustRegister("MigrationTarget", 2, func(ctx facade.Context) (facade.Facade, error) { - return newFacade(ctx) - }, reflect.TypeOf((*API)(nil))) +func Register(requiredMigrationFacadeVersions facades.FacadeVersions) func(registry facade.FacadeRegistry) { + return func(registry facade.FacadeRegistry) { + registry.MustRegister("MigrationTarget", 1, func(ctx facade.Context) (facade.Facade, error) { + return newFacadeV1(ctx) + }, reflect.TypeOf((*APIV1)(nil))) + registry.MustRegister("MigrationTarget", 2, func(ctx facade.Context) (facade.Facade, error) { + return newFacadeV2(ctx) + }, reflect.TypeOf((*APIV2)(nil))) + registry.MustRegister("MigrationTarget", 3, func(ctx facade.Context) (facade.Facade, error) { + return newFacade(ctx, requiredMigrationFacadeVersions) + }, reflect.TypeOf((*API)(nil))) + } } // newFacadeV1 is used for APIV1 registration. func newFacadeV1(ctx facade.Context) (*APIV1, error) { - api, err := newFacade(ctx) + api, err := newFacade(ctx, facades.FacadeVersions{}) if err != nil { return nil, errors.Trace(err) } return &APIV1{API: api}, nil } +// newFacadeV2 is used for APIV2 registration. +func newFacadeV2(ctx facade.Context) (*APIV2, error) { + api, err := newFacade(ctx, facades.FacadeVersions{}) + if err != nil { + return nil, errors.Trace(err) + } + return &APIV2{APIV1: &APIV1{API: api}}, nil +} + // newFacade is used for API registration. -func newFacade(ctx facade.Context) (*API, error) { +func newFacade(ctx facade.Context, facadeVersions facades.FacadeVersions) (*API, error) { auth := ctx.Auth() st := ctx.State() if err := checkAuth(auth, st); err != nil { @@ -94,5 +109,7 @@ func newFacade(ctx facade.Context) (*API, error) { credentialCallContextGetter, credentialcommon.CredentialInvalidatorGetter(ctx), stateenvirons.GetNewEnvironFunc(environs.New), - stateenvirons.GetNewCAASBrokerFunc(caas.New)) + stateenvirons.GetNewCAASBrokerFunc(caas.New), + facadeVersions, + ) } diff --git a/apiserver/facades/schema.json b/apiserver/facades/schema.json index 9cb917ee1a0..bbca754ec34 100644 --- a/apiserver/facades/schema.json +++ b/apiserver/facades/schema.json @@ -25965,6 +25965,17 @@ "controller-agent-version": { "$ref": "#/definitions/Number" }, + "facade-versions": { + "type": "object", + "patternProperties": { + ".*": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "name": { "type": "string" }, @@ -26517,7 +26528,7 @@ { "Name": "MigrationTarget", "Description": "API implements the API required for the model migration\nmaster worker when communicating with the target controller.", - "Version": 2, + "Version": 3, "AvailableTo": [ "controller-user" ], @@ -26731,6 +26742,17 @@ "controller-agent-version": { "$ref": "#/definitions/Number" }, + "facade-versions": { + "type": "object", + "patternProperties": { + ".*": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "name": { "type": "string" }, @@ -44031,6 +44053,12 @@ "$ref": "#/definitions/GrantRevokeSecretArg" } }, + "secret-track-latest": { + "type": "array", + "items": { + "type": "string" + } + }, "secret-updates": { "type": "array", "items": { diff --git a/apiserver/facadeversions_test.go b/apiserver/facadeversions_test.go index 502c601a3af..95d1a61f15d 100644 --- a/apiserver/facadeversions_test.go +++ b/apiserver/facadeversions_test.go @@ -10,6 +10,7 @@ import ( "github.com/juju/juju/api" "github.com/juju/juju/apiserver" + "github.com/juju/juju/core/facades" coretesting "github.com/juju/juju/testing" ) @@ -24,7 +25,7 @@ func (s *facadeVersionSuite) TestFacadeVersionsMatchServerVersions(c *gc.C) { // code just to list out what versions are available. However, we do // want to make sure that the two sides are kept in sync. clientFacadeNames := set.NewStrings() - for name, versions := range api.FacadeVersions { + for name, versions := range api.SupportedFacadeVersions() { clientFacadeNames.Add(name) // All versions should now be non-zero. c.Check(set.NewInts(versions...).Contains(0), jc.IsFalse) @@ -43,7 +44,7 @@ func (s *facadeVersionSuite) TestFacadeVersionsMatchServerVersions(c *gc.C) { // Next check that the latest version of each facade is the same // on both sides. apiFacadeVersions := make(map[string]int) - for name, versions := range api.FacadeVersions { + for name, versions := range api.SupportedFacadeVersions() { // Sort the versions so that we can easily pick the latest, without // a requirement that the versions are listed in order. sorted := set.NewInts(versions...).SortedValues() @@ -52,29 +53,29 @@ func (s *facadeVersionSuite) TestFacadeVersionsMatchServerVersions(c *gc.C) { c.Check(apiFacadeVersions, jc.DeepEquals, serverFacadeBestVersions) } -// TestClient3xSupport checks that the client facade supports the 3.x for -// certain tasks. You must be very careful when removing support for facades +// TestClientSupport checks that the client facade supports the 3.x and 4.x +// for certain tasks. You must be very careful when removing support for facades // as it can break model migrations, upgrades, and state reports. -func (s *facadeVersionSuite) TestClient3xSupport(c *gc.C) { +func (s *facadeVersionSuite) TestClientSupport(c *gc.C) { tests := []struct { facadeName string summary string - apiClientVersion int + apiClientVersion facades.FacadeVersion }{ { facadeName: "Client", summary: "Ensure that the Client facade supports 3.x for status requests", - apiClientVersion: 6, + apiClientVersion: []int{6}, }, { facadeName: "ModelManager", summary: "Ensure that the ModelManager facade supports 3.x for model migration and status requests", - apiClientVersion: 9, + apiClientVersion: []int{9}, }, } for _, test := range tests { c.Logf(test.summary) - c.Check(api.FacadeVersions[test.facadeName], Contains, test.apiClientVersion) + c.Check(api.SupportedFacadeVersions()[test.facadeName], Contains, test.apiClientVersion) } } @@ -88,18 +89,17 @@ var Contains gc.Checker = &containsChecker{ } func (checker *containsChecker) Check(params []interface{}, names []string) (result bool, err string) { - expected, ok := params[1].(int) + expected, ok := params[1].(facades.FacadeVersion) if !ok { - return false, "expected must be a string" + return false, "expected must be a int" } - obtained, ok := params[0].([]int) + obtained, ok := params[0].(facades.FacadeVersion) if ok { - for _, v := range obtained { - if v == expected { - return true, "" - } + if set.NewInts(expected...).Intersection(set.NewInts(obtained...)).Size() > 0 { + return true, "" } + return false, "" } diff --git a/caas/kubernetes/provider/application/application.go b/caas/kubernetes/provider/application/application.go index 492d3d8e478..dbbfb5721c8 100644 --- a/caas/kubernetes/provider/application/application.go +++ b/caas/kubernetes/provider/application/application.go @@ -658,6 +658,11 @@ func (a *app) configureHeadlessService(name string, annotation annotations.Annot return svc.Apply(context.Background(), a.client) } +const ( + placeholderPortName = "placeholder" + placeholderPort = 65535 +) + // configureDefaultService configures the default service for the application. // It's only configured once when the application was deployed in the first time. func (a *app) configureDefaultService(annotation annotations.Annotation) (err error) { @@ -670,8 +675,8 @@ func (a *app) configureDefaultService(annotation annotations.Annotation) (err er Selector: a.selectorLabels(), Type: corev1.ServiceTypeClusterIP, Ports: []corev1.ServicePort{{ - Name: "placeholder", - Port: 65535, + Name: placeholderPortName, + Port: placeholderPort, }}, }, }) @@ -749,10 +754,16 @@ func (a *app) UpdatePorts(ports []caas.ServicePort, updateContainerPorts bool) e var expectedPorts []corev1.ServicePort for _, p := range svc.Service.Spec.Ports { - if !strings.HasPrefix(p.Name, portNamePrefix) { - // The ports are not mamanged by Juju should be kept. - expectedPorts = append(expectedPorts, p) + if p.Name == placeholderPortName && len(ports) > 0 { + // Ignore placeholder port if there are ports supplied by the charm. + continue + } + if strings.HasPrefix(p.Name, portNamePrefix) { + // Port managed by Juju - will be replaced, + continue } + // The ports that are not managed by Juju should be kept. + expectedPorts = append(expectedPorts, p) } for _, port := range ports { sp, err := convertServicePort(port) diff --git a/caas/kubernetes/provider/application/application_test.go b/caas/kubernetes/provider/application/application_test.go index 277628da529..cb9a87dcef7 100644 --- a/caas/kubernetes/provider/application/application_test.go +++ b/caas/kubernetes/provider/application/application_test.go @@ -1474,6 +1474,10 @@ func getDefaultSvc() *corev1.Service { Selector: map[string]string{"app.kubernetes.io/name": "gitlab"}, Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{{ + Name: "placeholder", + Port: 65535, + }}, }, } } diff --git a/caas/kubernetes/provider/application/constraints.go b/caas/kubernetes/provider/application/constraints.go index b884775e6e1..b3120f7c45f 100644 --- a/caas/kubernetes/provider/application/constraints.go +++ b/caas/kubernetes/provider/application/constraints.go @@ -245,7 +245,7 @@ func processPodAffinity(pod *core.PodSpec, affinityLabels map[string]string) err } var affinityTerm core.PodAffinityTerm updateAffinityTerm(&affinityTerm, affinityTags) - if len(affinityTerm.LabelSelector.MatchExpressions) > 0 { + if len(affinityTerm.LabelSelector.MatchExpressions) > 0 || affinityTerm.TopologyKey != "" { pod.Affinity.PodAffinity = &core.PodAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{affinityTerm}, } @@ -253,7 +253,7 @@ func processPodAffinity(pod *core.PodSpec, affinityLabels map[string]string) err var antiAffinityTerm core.PodAffinityTerm updateAffinityTerm(&antiAffinityTerm, antiAffinityTags) - if len(antiAffinityTerm.LabelSelector.MatchExpressions) > 0 { + if len(antiAffinityTerm.LabelSelector.MatchExpressions) > 0 || antiAffinityTerm.TopologyKey != "" { pod.Affinity.PodAntiAffinity = &core.PodAntiAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{antiAffinityTerm}, } diff --git a/caas/kubernetes/provider/application/constraints_test.go b/caas/kubernetes/provider/application/constraints_test.go new file mode 100644 index 00000000000..ce39bb93c58 --- /dev/null +++ b/caas/kubernetes/provider/application/constraints_test.go @@ -0,0 +1,213 @@ +// Copyright 2023 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package application_test + +import ( + "errors" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/juju/juju/caas/kubernetes/provider/application" + "github.com/juju/juju/core/constraints" + "github.com/juju/juju/testing" +) + +type applyConstraintsSuite struct { + testing.BaseSuite +} + +var _ = gc.Suite(&applyConstraintsSuite{}) + +func (s *applyConstraintsSuite) TestMemory(c *gc.C) { + pod := &corev1.PodSpec{} + configureConstraint := func(got *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + c.Assert(got, gc.Equals, pod) + c.Assert(resourceName, gc.Equals, corev1.ResourceName("memory")) + c.Assert(value, gc.Equals, "4096Mi") + return errors.New("boom") + } + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("mem=4G"), configureConstraint) + c.Assert(err, gc.ErrorMatches, "configuring memory constraint for foo: boom") +} + +func (s *applyConstraintsSuite) TestCPU(c *gc.C) { + pod := &corev1.PodSpec{} + configureConstraint := func(got *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + c.Assert(got, gc.Equals, pod) + c.Assert(resourceName, gc.Equals, corev1.ResourceName("cpu")) + c.Assert(value, gc.Equals, "2m") + return errors.New("boom") + } + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("cpu-power=2"), configureConstraint) + c.Assert(err, gc.ErrorMatches, "configuring cpu constraint for foo: boom") +} + +func (s *applyConstraintsSuite) TestArch(c *gc.C) { + configureConstraint := func(got *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("arch=arm64"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.NodeSelector, jc.DeepEquals, map[string]string{"kubernetes.io/arch": "arm64"}) +} + +func (s *applyConstraintsSuite) TestPodAffinityJustTopologyKey(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=pod.topology-key=foo"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAffinity, jc.DeepEquals, &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{}, + TopologyKey: "foo", + }}, + }) +} + +func (s *applyConstraintsSuite) TestAffinityPod(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=pod.hello=world|universe,pod.^goodbye=world"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAffinity, jc.DeepEquals, &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "goodbye", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"world"}, + }, { + Key: "hello", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"world", "universe"}, + }}, + }, + }}, + }) +} + +func (s *applyConstraintsSuite) TestPodAffinityAll(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=pod.hello=world,pod.^goodbye=world,pod.topology-key=foo"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAffinity, jc.DeepEquals, &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "goodbye", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"world"}, + }, { + Key: "hello", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"world"}, + }}, + }, + TopologyKey: "foo", + }}, + }) +} + +func (s *applyConstraintsSuite) TestAntiPodAffinityJustTopologyKey(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=anti-pod.topology-key=foo"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAntiAffinity, jc.DeepEquals, &corev1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{}, + TopologyKey: "foo", + }}, + }) +} + +func (s *applyConstraintsSuite) TestAntiPodAffinity(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=anti-pod.hello=world|universe,anti-pod.^goodbye=world"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAntiAffinity, jc.DeepEquals, &corev1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "goodbye", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"world"}, + }, { + Key: "hello", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"world", "universe"}, + }}, + }, + }}, + }) +} + +func (s *applyConstraintsSuite) TestAntiPodAffinityAll(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=anti-pod.hello=world,anti-pod.^goodbye=world,anti-pod.topology-key=foo"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.PodAntiAffinity, jc.DeepEquals, &corev1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "goodbye", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"world"}, + }, { + Key: "hello", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"world"}, + }}, + }, + TopologyKey: "foo", + }}, + }) +} + +func (s *applyConstraintsSuite) TestNodeAntiAffinity(c *gc.C) { + configureConstraint := func(pod *corev1.PodSpec, resourceName corev1.ResourceName, value string) (err error) { + return errors.New("unexpected") + } + pod := &corev1.PodSpec{} + err := application.ApplyConstraints(pod, "foo", constraints.MustParse("tags=node.hello=world|universe,node.^goodbye=world"), configureConstraint) + c.Assert(err, jc.ErrorIsNil) + c.Assert(pod.Affinity.NodeAffinity, jc.DeepEquals, &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{{ + MatchExpressions: []corev1.NodeSelectorRequirement{{ + Key: "goodbye", + Operator: corev1.NodeSelectorOpNotIn, + Values: []string{"world"}, + }, { + Key: "hello", + Operator: corev1.NodeSelectorOpIn, + Values: []string{"world", "universe"}, + }}, + }}, + }, + }) +} diff --git a/caas/kubernetes/provider/exec/mocks/remotecommand_mock.go b/caas/kubernetes/provider/exec/mocks/remotecommand_mock.go index 9217e4c36ca..b0cb2d53bbc 100644 --- a/caas/kubernetes/provider/exec/mocks/remotecommand_mock.go +++ b/caas/kubernetes/provider/exec/mocks/remotecommand_mock.go @@ -5,6 +5,7 @@ package mocks import ( + context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -47,3 +48,17 @@ func (mr *MockExecutorMockRecorder) Stream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stream", reflect.TypeOf((*MockExecutor)(nil).Stream), arg0) } + +// StreamWithContext mocks base method. +func (m *MockExecutor) StreamWithContext(arg0 context.Context, arg1 remotecommand.StreamOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StreamWithContext", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// StreamWithContext indicates an expected call of StreamWithContext. +func (mr *MockExecutorMockRecorder) StreamWithContext(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamWithContext", reflect.TypeOf((*MockExecutor)(nil).StreamWithContext), arg0, arg1) +} diff --git a/caas/kubernetes/provider/mocks/admissionregistrationv1beta1_mock.go b/caas/kubernetes/provider/mocks/admissionregistrationv1beta1_mock.go index 4db0930abce..2dc1bfee511 100644 --- a/caas/kubernetes/provider/mocks/admissionregistrationv1beta1_mock.go +++ b/caas/kubernetes/provider/mocks/admissionregistrationv1beta1_mock.go @@ -69,6 +69,34 @@ func (mr *MockAdmissionregistrationV1beta1InterfaceMockRecorder) RESTClient() *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTClient", reflect.TypeOf((*MockAdmissionregistrationV1beta1Interface)(nil).RESTClient)) } +// ValidatingAdmissionPolicies mocks base method. +func (m *MockAdmissionregistrationV1beta1Interface) ValidatingAdmissionPolicies() v1beta11.ValidatingAdmissionPolicyInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatingAdmissionPolicies") + ret0, _ := ret[0].(v1beta11.ValidatingAdmissionPolicyInterface) + return ret0 +} + +// ValidatingAdmissionPolicies indicates an expected call of ValidatingAdmissionPolicies. +func (mr *MockAdmissionregistrationV1beta1InterfaceMockRecorder) ValidatingAdmissionPolicies() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatingAdmissionPolicies", reflect.TypeOf((*MockAdmissionregistrationV1beta1Interface)(nil).ValidatingAdmissionPolicies)) +} + +// ValidatingAdmissionPolicyBindings mocks base method. +func (m *MockAdmissionregistrationV1beta1Interface) ValidatingAdmissionPolicyBindings() v1beta11.ValidatingAdmissionPolicyBindingInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatingAdmissionPolicyBindings") + ret0, _ := ret[0].(v1beta11.ValidatingAdmissionPolicyBindingInterface) + return ret0 +} + +// ValidatingAdmissionPolicyBindings indicates an expected call of ValidatingAdmissionPolicyBindings. +func (mr *MockAdmissionregistrationV1beta1InterfaceMockRecorder) ValidatingAdmissionPolicyBindings() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatingAdmissionPolicyBindings", reflect.TypeOf((*MockAdmissionregistrationV1beta1Interface)(nil).ValidatingAdmissionPolicyBindings)) +} + // ValidatingWebhookConfigurations mocks base method. func (m *MockAdmissionregistrationV1beta1Interface) ValidatingWebhookConfigurations() v1beta11.ValidatingWebhookConfigurationInterface { m.ctrl.T.Helper() diff --git a/caas/kubernetes/provider/mocks/discovery_mock.go b/caas/kubernetes/provider/mocks/discovery_mock.go index 05ab6cd24e7..f524180b35c 100644 --- a/caas/kubernetes/provider/mocks/discovery_mock.go +++ b/caas/kubernetes/provider/mocks/discovery_mock.go @@ -7,10 +7,12 @@ package mocks import ( reflect "reflect" - openapi_v2 "github.com/googleapis/gnostic/openapiv2" + openapi_v2 "github.com/google/gnostic-models/openapiv2" gomock "go.uber.org/mock/gomock" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" version "k8s.io/apimachinery/pkg/version" + discovery "k8s.io/client-go/discovery" + openapi "k8s.io/client-go/openapi" rest "k8s.io/client-go/rest" ) @@ -52,6 +54,20 @@ func (mr *MockDiscoveryInterfaceMockRecorder) OpenAPISchema() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenAPISchema", reflect.TypeOf((*MockDiscoveryInterface)(nil).OpenAPISchema)) } +// OpenAPIV3 mocks base method. +func (m *MockDiscoveryInterface) OpenAPIV3() openapi.Client { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OpenAPIV3") + ret0, _ := ret[0].(openapi.Client) + return ret0 +} + +// OpenAPIV3 indicates an expected call of OpenAPIV3. +func (mr *MockDiscoveryInterfaceMockRecorder) OpenAPIV3() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenAPIV3", reflect.TypeOf((*MockDiscoveryInterface)(nil).OpenAPIV3)) +} + // RESTClient mocks base method. func (m *MockDiscoveryInterface) RESTClient() rest.Interface { m.ctrl.T.Helper() @@ -127,21 +143,6 @@ func (mr *MockDiscoveryInterfaceMockRecorder) ServerPreferredResources() *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerPreferredResources", reflect.TypeOf((*MockDiscoveryInterface)(nil).ServerPreferredResources)) } -// ServerResources mocks base method. -func (m *MockDiscoveryInterface) ServerResources() ([]*v1.APIResourceList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServerResources") - ret0, _ := ret[0].([]*v1.APIResourceList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServerResources indicates an expected call of ServerResources. -func (mr *MockDiscoveryInterfaceMockRecorder) ServerResources() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerResources", reflect.TypeOf((*MockDiscoveryInterface)(nil).ServerResources)) -} - // ServerResourcesForGroupVersion mocks base method. func (m *MockDiscoveryInterface) ServerResourcesForGroupVersion(arg0 string) (*v1.APIResourceList, error) { m.ctrl.T.Helper() @@ -171,3 +172,17 @@ func (mr *MockDiscoveryInterfaceMockRecorder) ServerVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerVersion", reflect.TypeOf((*MockDiscoveryInterface)(nil).ServerVersion)) } + +// WithLegacy mocks base method. +func (m *MockDiscoveryInterface) WithLegacy() discovery.DiscoveryInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithLegacy") + ret0, _ := ret[0].(discovery.DiscoveryInterface) + return ret0 +} + +// WithLegacy indicates an expected call of WithLegacy. +func (mr *MockDiscoveryInterfaceMockRecorder) WithLegacy() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithLegacy", reflect.TypeOf((*MockDiscoveryInterface)(nil).WithLegacy)) +} diff --git a/caas/kubernetes/provider/mocks/dynamic_mock.go b/caas/kubernetes/provider/mocks/dynamic_mock.go index e668515a004..71b2c927275 100644 --- a/caas/kubernetes/provider/mocks/dynamic_mock.go +++ b/caas/kubernetes/provider/mocks/dynamic_mock.go @@ -77,6 +77,41 @@ func (m *MockResourceInterface) EXPECT() *MockResourceInterfaceMockRecorder { return m.recorder } +// Apply mocks base method. +func (m *MockResourceInterface) Apply(arg0 context.Context, arg1 string, arg2 *unstructured.Unstructured, arg3 v1.ApplyOptions, arg4 ...string) (*unstructured.Unstructured, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Apply", varargs...) + ret0, _ := ret[0].(*unstructured.Unstructured) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Apply indicates an expected call of Apply. +func (mr *MockResourceInterfaceMockRecorder) Apply(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockResourceInterface)(nil).Apply), varargs...) +} + +// ApplyStatus mocks base method. +func (m *MockResourceInterface) ApplyStatus(arg0 context.Context, arg1 string, arg2 *unstructured.Unstructured, arg3 v1.ApplyOptions) (*unstructured.Unstructured, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyStatus", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*unstructured.Unstructured) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyStatus indicates an expected call of ApplyStatus. +func (mr *MockResourceInterfaceMockRecorder) ApplyStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyStatus", reflect.TypeOf((*MockResourceInterface)(nil).ApplyStatus), arg0, arg1, arg2, arg3) +} + // Create mocks base method. func (m *MockResourceInterface) Create(arg0 context.Context, arg1 *unstructured.Unstructured, arg2 v1.CreateOptions, arg3 ...string) (*unstructured.Unstructured, error) { m.ctrl.T.Helper() @@ -258,6 +293,41 @@ func (m *MockNamespaceableResourceInterface) EXPECT() *MockNamespaceableResource return m.recorder } +// Apply mocks base method. +func (m *MockNamespaceableResourceInterface) Apply(arg0 context.Context, arg1 string, arg2 *unstructured.Unstructured, arg3 v1.ApplyOptions, arg4 ...string) (*unstructured.Unstructured, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Apply", varargs...) + ret0, _ := ret[0].(*unstructured.Unstructured) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Apply indicates an expected call of Apply. +func (mr *MockNamespaceableResourceInterfaceMockRecorder) Apply(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockNamespaceableResourceInterface)(nil).Apply), varargs...) +} + +// ApplyStatus mocks base method. +func (m *MockNamespaceableResourceInterface) ApplyStatus(arg0 context.Context, arg1 string, arg2 *unstructured.Unstructured, arg3 v1.ApplyOptions) (*unstructured.Unstructured, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyStatus", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*unstructured.Unstructured) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyStatus indicates an expected call of ApplyStatus. +func (mr *MockNamespaceableResourceInterfaceMockRecorder) ApplyStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyStatus", reflect.TypeOf((*MockNamespaceableResourceInterface)(nil).ApplyStatus), arg0, arg1, arg2, arg3) +} + // Create mocks base method. func (m *MockNamespaceableResourceInterface) Create(arg0 context.Context, arg1 *unstructured.Unstructured, arg2 v1.CreateOptions, arg3 ...string) (*unstructured.Unstructured, error) { m.ctrl.T.Helper() diff --git a/caas/kubernetes/provider/mocks/k8sclient_mock.go b/caas/kubernetes/provider/mocks/k8sclient_mock.go index 3890552e7af..93adfa53b60 100644 --- a/caas/kubernetes/provider/mocks/k8sclient_mock.go +++ b/caas/kubernetes/provider/mocks/k8sclient_mock.go @@ -10,12 +10,14 @@ import ( gomock "go.uber.org/mock/gomock" discovery "k8s.io/client-go/discovery" v1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" + v1alpha1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1" v1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" - v1alpha1 "k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1" + v1alpha10 "k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1" v10 "k8s.io/client-go/kubernetes/typed/apps/v1" v1beta10 "k8s.io/client-go/kubernetes/typed/apps/v1beta1" v1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" v11 "k8s.io/client-go/kubernetes/typed/authentication/v1" + v1alpha11 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1" v1beta11 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" v12 "k8s.io/client-go/kubernetes/typed/authorization/v1" v1beta12 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" @@ -26,6 +28,7 @@ import ( v14 "k8s.io/client-go/kubernetes/typed/batch/v1" v1beta13 "k8s.io/client-go/kubernetes/typed/batch/v1beta1" v15 "k8s.io/client-go/kubernetes/typed/certificates/v1" + v1alpha12 "k8s.io/client-go/kubernetes/typed/certificates/v1alpha1" v1beta14 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" v16 "k8s.io/client-go/kubernetes/typed/coordination/v1" v1beta15 "k8s.io/client-go/kubernetes/typed/coordination/v1beta1" @@ -35,24 +38,27 @@ import ( v19 "k8s.io/client-go/kubernetes/typed/events/v1" v1beta17 "k8s.io/client-go/kubernetes/typed/events/v1beta1" v1beta18 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" - v1alpha10 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1" + v1alpha13 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1" v1beta19 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1" v1beta20 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2" + v1beta3 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3" v110 "k8s.io/client-go/kubernetes/typed/networking/v1" + v1alpha14 "k8s.io/client-go/kubernetes/typed/networking/v1alpha1" v1beta110 "k8s.io/client-go/kubernetes/typed/networking/v1beta1" v111 "k8s.io/client-go/kubernetes/typed/node/v1" - v1alpha11 "k8s.io/client-go/kubernetes/typed/node/v1alpha1" + v1alpha15 "k8s.io/client-go/kubernetes/typed/node/v1alpha1" v1beta111 "k8s.io/client-go/kubernetes/typed/node/v1beta1" v112 "k8s.io/client-go/kubernetes/typed/policy/v1" v1beta112 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" v113 "k8s.io/client-go/kubernetes/typed/rbac/v1" - v1alpha12 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1" + v1alpha16 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1" v1beta113 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" + v1alpha2 "k8s.io/client-go/kubernetes/typed/resource/v1alpha2" v114 "k8s.io/client-go/kubernetes/typed/scheduling/v1" - v1alpha13 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1" + v1alpha17 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1" v1beta114 "k8s.io/client-go/kubernetes/typed/scheduling/v1beta1" v115 "k8s.io/client-go/kubernetes/typed/storage/v1" - v1alpha14 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1" + v1alpha18 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1" v1beta115 "k8s.io/client-go/kubernetes/typed/storage/v1beta1" ) @@ -93,6 +99,20 @@ func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1)) } +// AdmissionregistrationV1alpha1 mocks base method. +func (m *MockInterface) AdmissionregistrationV1alpha1() v1alpha1.AdmissionregistrationV1alpha1Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AdmissionregistrationV1alpha1") + ret0, _ := ret[0].(v1alpha1.AdmissionregistrationV1alpha1Interface) + return ret0 +} + +// AdmissionregistrationV1alpha1 indicates an expected call of AdmissionregistrationV1alpha1. +func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1alpha1() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1alpha1)) +} + // AdmissionregistrationV1beta1 mocks base method. func (m *MockInterface) AdmissionregistrationV1beta1() v1beta1.AdmissionregistrationV1beta1Interface { m.ctrl.T.Helper() @@ -163,6 +183,20 @@ func (mr *MockInterfaceMockRecorder) AuthenticationV1() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1)) } +// AuthenticationV1alpha1 mocks base method. +func (m *MockInterface) AuthenticationV1alpha1() v1alpha11.AuthenticationV1alpha1Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AuthenticationV1alpha1") + ret0, _ := ret[0].(v1alpha11.AuthenticationV1alpha1Interface) + return ret0 +} + +// AuthenticationV1alpha1 indicates an expected call of AuthenticationV1alpha1. +func (mr *MockInterfaceMockRecorder) AuthenticationV1alpha1() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1alpha1)) +} + // AuthenticationV1beta1 mocks base method. func (m *MockInterface) AuthenticationV1beta1() v1beta11.AuthenticationV1beta1Interface { m.ctrl.T.Helper() @@ -303,6 +337,20 @@ func (mr *MockInterfaceMockRecorder) CertificatesV1() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1)) } +// CertificatesV1alpha1 mocks base method. +func (m *MockInterface) CertificatesV1alpha1() v1alpha12.CertificatesV1alpha1Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CertificatesV1alpha1") + ret0, _ := ret[0].(v1alpha12.CertificatesV1alpha1Interface) + return ret0 +} + +// CertificatesV1alpha1 indicates an expected call of CertificatesV1alpha1. +func (mr *MockInterfaceMockRecorder) CertificatesV1alpha1() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1alpha1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1alpha1)) +} + // CertificatesV1beta1 mocks base method. func (m *MockInterface) CertificatesV1beta1() v1beta14.CertificatesV1beta1Interface { m.ctrl.T.Helper() @@ -444,10 +492,10 @@ func (mr *MockInterfaceMockRecorder) ExtensionsV1beta1() *gomock.Call { } // FlowcontrolV1alpha1 mocks base method. -func (m *MockInterface) FlowcontrolV1alpha1() v1alpha10.FlowcontrolV1alpha1Interface { +func (m *MockInterface) FlowcontrolV1alpha1() v1alpha13.FlowcontrolV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlowcontrolV1alpha1") - ret0, _ := ret[0].(v1alpha10.FlowcontrolV1alpha1Interface) + ret0, _ := ret[0].(v1alpha13.FlowcontrolV1alpha1Interface) return ret0 } @@ -485,11 +533,25 @@ func (mr *MockInterfaceMockRecorder) FlowcontrolV1beta2() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1beta2", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1beta2)) } +// FlowcontrolV1beta3 mocks base method. +func (m *MockInterface) FlowcontrolV1beta3() v1beta3.FlowcontrolV1beta3Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FlowcontrolV1beta3") + ret0, _ := ret[0].(v1beta3.FlowcontrolV1beta3Interface) + return ret0 +} + +// FlowcontrolV1beta3 indicates an expected call of FlowcontrolV1beta3. +func (mr *MockInterfaceMockRecorder) FlowcontrolV1beta3() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1beta3", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1beta3)) +} + // InternalV1alpha1 mocks base method. -func (m *MockInterface) InternalV1alpha1() v1alpha1.InternalV1alpha1Interface { +func (m *MockInterface) InternalV1alpha1() v1alpha10.InternalV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InternalV1alpha1") - ret0, _ := ret[0].(v1alpha1.InternalV1alpha1Interface) + ret0, _ := ret[0].(v1alpha10.InternalV1alpha1Interface) return ret0 } @@ -513,6 +575,20 @@ func (mr *MockInterfaceMockRecorder) NetworkingV1() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1)) } +// NetworkingV1alpha1 mocks base method. +func (m *MockInterface) NetworkingV1alpha1() v1alpha14.NetworkingV1alpha1Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetworkingV1alpha1") + ret0, _ := ret[0].(v1alpha14.NetworkingV1alpha1Interface) + return ret0 +} + +// NetworkingV1alpha1 indicates an expected call of NetworkingV1alpha1. +func (mr *MockInterfaceMockRecorder) NetworkingV1alpha1() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1alpha1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1alpha1)) +} + // NetworkingV1beta1 mocks base method. func (m *MockInterface) NetworkingV1beta1() v1beta110.NetworkingV1beta1Interface { m.ctrl.T.Helper() @@ -542,10 +618,10 @@ func (mr *MockInterfaceMockRecorder) NodeV1() *gomock.Call { } // NodeV1alpha1 mocks base method. -func (m *MockInterface) NodeV1alpha1() v1alpha11.NodeV1alpha1Interface { +func (m *MockInterface) NodeV1alpha1() v1alpha15.NodeV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NodeV1alpha1") - ret0, _ := ret[0].(v1alpha11.NodeV1alpha1Interface) + ret0, _ := ret[0].(v1alpha15.NodeV1alpha1Interface) return ret0 } @@ -612,10 +688,10 @@ func (mr *MockInterfaceMockRecorder) RbacV1() *gomock.Call { } // RbacV1alpha1 mocks base method. -func (m *MockInterface) RbacV1alpha1() v1alpha12.RbacV1alpha1Interface { +func (m *MockInterface) RbacV1alpha1() v1alpha16.RbacV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RbacV1alpha1") - ret0, _ := ret[0].(v1alpha12.RbacV1alpha1Interface) + ret0, _ := ret[0].(v1alpha16.RbacV1alpha1Interface) return ret0 } @@ -639,6 +715,20 @@ func (mr *MockInterfaceMockRecorder) RbacV1beta1() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1beta1", reflect.TypeOf((*MockInterface)(nil).RbacV1beta1)) } +// ResourceV1alpha2 mocks base method. +func (m *MockInterface) ResourceV1alpha2() v1alpha2.ResourceV1alpha2Interface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResourceV1alpha2") + ret0, _ := ret[0].(v1alpha2.ResourceV1alpha2Interface) + return ret0 +} + +// ResourceV1alpha2 indicates an expected call of ResourceV1alpha2. +func (mr *MockInterfaceMockRecorder) ResourceV1alpha2() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceV1alpha2", reflect.TypeOf((*MockInterface)(nil).ResourceV1alpha2)) +} + // SchedulingV1 mocks base method. func (m *MockInterface) SchedulingV1() v114.SchedulingV1Interface { m.ctrl.T.Helper() @@ -654,10 +744,10 @@ func (mr *MockInterfaceMockRecorder) SchedulingV1() *gomock.Call { } // SchedulingV1alpha1 mocks base method. -func (m *MockInterface) SchedulingV1alpha1() v1alpha13.SchedulingV1alpha1Interface { +func (m *MockInterface) SchedulingV1alpha1() v1alpha17.SchedulingV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SchedulingV1alpha1") - ret0, _ := ret[0].(v1alpha13.SchedulingV1alpha1Interface) + ret0, _ := ret[0].(v1alpha17.SchedulingV1alpha1Interface) return ret0 } @@ -696,10 +786,10 @@ func (mr *MockInterfaceMockRecorder) StorageV1() *gomock.Call { } // StorageV1alpha1 mocks base method. -func (m *MockInterface) StorageV1alpha1() v1alpha14.StorageV1alpha1Interface { +func (m *MockInterface) StorageV1alpha1() v1alpha18.StorageV1alpha1Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StorageV1alpha1") - ret0, _ := ret[0].(v1alpha14.StorageV1alpha1Interface) + ret0, _ := ret[0].(v1alpha18.StorageV1alpha1Interface) return ret0 } diff --git a/caas/kubernetes/provider/mocks/sharedindexinformer_mock.go b/caas/kubernetes/provider/mocks/sharedindexinformer_mock.go index 750d31c932b..f34343eed2c 100644 --- a/caas/kubernetes/provider/mocks/sharedindexinformer_mock.go +++ b/caas/kubernetes/provider/mocks/sharedindexinformer_mock.go @@ -36,9 +36,12 @@ func (m *MockSharedIndexInformer) EXPECT() *MockSharedIndexInformerMockRecorder } // AddEventHandler mocks base method. -func (m *MockSharedIndexInformer) AddEventHandler(arg0 cache.ResourceEventHandler) { +func (m *MockSharedIndexInformer) AddEventHandler(arg0 cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddEventHandler", arg0) + ret := m.ctrl.Call(m, "AddEventHandler", arg0) + ret0, _ := ret[0].(cache.ResourceEventHandlerRegistration) + ret1, _ := ret[1].(error) + return ret0, ret1 } // AddEventHandler indicates an expected call of AddEventHandler. @@ -48,9 +51,12 @@ func (mr *MockSharedIndexInformerMockRecorder) AddEventHandler(arg0 interface{}) } // AddEventHandlerWithResyncPeriod mocks base method. -func (m *MockSharedIndexInformer) AddEventHandlerWithResyncPeriod(arg0 cache.ResourceEventHandler, arg1 time.Duration) { +func (m *MockSharedIndexInformer) AddEventHandlerWithResyncPeriod(arg0 cache.ResourceEventHandler, arg1 time.Duration) (cache.ResourceEventHandlerRegistration, error) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddEventHandlerWithResyncPeriod", arg0, arg1) + ret := m.ctrl.Call(m, "AddEventHandlerWithResyncPeriod", arg0, arg1) + ret0, _ := ret[0].(cache.ResourceEventHandlerRegistration) + ret1, _ := ret[1].(error) + return ret0, ret1 } // AddEventHandlerWithResyncPeriod indicates an expected call of AddEventHandlerWithResyncPeriod. @@ -129,6 +135,20 @@ func (mr *MockSharedIndexInformerMockRecorder) HasSynced() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasSynced", reflect.TypeOf((*MockSharedIndexInformer)(nil).HasSynced)) } +// IsStopped mocks base method. +func (m *MockSharedIndexInformer) IsStopped() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsStopped") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsStopped indicates an expected call of IsStopped. +func (mr *MockSharedIndexInformerMockRecorder) IsStopped() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStopped", reflect.TypeOf((*MockSharedIndexInformer)(nil).IsStopped)) +} + // LastSyncResourceVersion mocks base method. func (m *MockSharedIndexInformer) LastSyncResourceVersion() string { m.ctrl.T.Helper() @@ -143,6 +163,20 @@ func (mr *MockSharedIndexInformerMockRecorder) LastSyncResourceVersion() *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastSyncResourceVersion", reflect.TypeOf((*MockSharedIndexInformer)(nil).LastSyncResourceVersion)) } +// RemoveEventHandler mocks base method. +func (m *MockSharedIndexInformer) RemoveEventHandler(arg0 cache.ResourceEventHandlerRegistration) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveEventHandler", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveEventHandler indicates an expected call of RemoveEventHandler. +func (mr *MockSharedIndexInformerMockRecorder) RemoveEventHandler(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveEventHandler", reflect.TypeOf((*MockSharedIndexInformer)(nil).RemoveEventHandler), arg0) +} + // Run mocks base method. func (m *MockSharedIndexInformer) Run(arg0 <-chan struct{}) { m.ctrl.T.Helper() @@ -155,6 +189,20 @@ func (mr *MockSharedIndexInformerMockRecorder) Run(arg0 interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockSharedIndexInformer)(nil).Run), arg0) } +// SetTransform mocks base method. +func (m *MockSharedIndexInformer) SetTransform(arg0 cache.TransformFunc) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTransform", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetTransform indicates an expected call of SetTransform. +func (mr *MockSharedIndexInformerMockRecorder) SetTransform(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTransform", reflect.TypeOf((*MockSharedIndexInformer)(nil).SetTransform), arg0) +} + // SetWatchErrorHandler mocks base method. func (m *MockSharedIndexInformer) SetWatchErrorHandler(arg0 cache.WatchErrorHandler) error { m.ctrl.T.Helper() diff --git a/caas/kubernetes/provider/mocks/storagev1_mock.go b/caas/kubernetes/provider/mocks/storagev1_mock.go index 697d91f7d01..e0add2ea075 100644 --- a/caas/kubernetes/provider/mocks/storagev1_mock.go +++ b/caas/kubernetes/provider/mocks/storagev1_mock.go @@ -69,6 +69,20 @@ func (mr *MockStorageV1InterfaceMockRecorder) CSINodes() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CSINodes", reflect.TypeOf((*MockStorageV1Interface)(nil).CSINodes)) } +// CSIStorageCapacities mocks base method. +func (m *MockStorageV1Interface) CSIStorageCapacities(arg0 string) v12.CSIStorageCapacityInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CSIStorageCapacities", arg0) + ret0, _ := ret[0].(v12.CSIStorageCapacityInterface) + return ret0 +} + +// CSIStorageCapacities indicates an expected call of CSIStorageCapacities. +func (mr *MockStorageV1InterfaceMockRecorder) CSIStorageCapacities(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CSIStorageCapacities", reflect.TypeOf((*MockStorageV1Interface)(nil).CSIStorageCapacities), arg0) +} + // RESTClient mocks base method. func (m *MockStorageV1Interface) RESTClient() rest.Interface { m.ctrl.T.Helper() diff --git a/caas/kubernetes/provider/storage_test.go b/caas/kubernetes/provider/storage_test.go index fbdad7a3a46..73c82d29e05 100644 --- a/caas/kubernetes/provider/storage_test.go +++ b/caas/kubernetes/provider/storage_test.go @@ -170,7 +170,7 @@ func (s *storageSuite) TestDescribeVolumes(c *gc.C) { result, err := vs.DescribeVolumes(envcontext.WithoutCredentialInvalidator(context.Background()), []string{"vol-id"}) c.Assert(err, jc.ErrorIsNil) c.Assert(result, jc.DeepEquals, []storage.DescribeVolumesResult{{ - VolumeInfo: &storage.VolumeInfo{VolumeId: "vol-id", Size: 68, Persistent: true}, + VolumeInfo: &storage.VolumeInfo{VolumeId: "vol-id", Size: 66, Persistent: true}, }}) } diff --git a/caas/kubernetes/provider/watcher/k8swatcher.go b/caas/kubernetes/provider/watcher/k8swatcher.go index c24e9df4ad3..982af2794f7 100644 --- a/caas/kubernetes/provider/watcher/k8swatcher.go +++ b/caas/kubernetes/provider/watcher/k8swatcher.go @@ -7,6 +7,7 @@ import ( "time" jujuclock "github.com/juju/clock" + "github.com/juju/errors" "github.com/juju/loggo" "github.com/juju/worker/v3/catacomb" "k8s.io/apimachinery/pkg/api/meta" @@ -84,13 +85,16 @@ func (w *kubernetesNotifyWatcher) loop() error { } } - w.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := w.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: fireFn("add"), DeleteFunc: fireFn("delete"), UpdateFunc: func(_, obj interface{}) { fireFn("update")(obj) }, }) + if err != nil { + return errors.Trace(err) + } // Set out now so that initial event is sent. out := w.out @@ -201,13 +205,16 @@ func (w *kubernetesStringsWatcher) loop() error { } } - w.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := w.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: fireFn(WatchEventAdd), DeleteFunc: fireFn(WatchEventDelete), UpdateFunc: func(_, obj interface{}) { fireFn(WatchEventUpdate)(obj) }, }) + if err != nil { + return errors.Trace(err) + } // Set out now so that initial event is sent. var out chan []string diff --git a/caas/kubernetes/tunnel.go b/caas/kubernetes/tunnel.go index 4a85dd5298e..8485c949e37 100644 --- a/caas/kubernetes/tunnel.go +++ b/caas/kubernetes/tunnel.go @@ -244,7 +244,7 @@ func (t *Tunnel) waitForPodReady(ctx context.Context, podName string) error { defer close(stopChan) defer close(eventChan) - informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { objPod, valid := obj.(*corev1.Pod) if !valid { @@ -279,6 +279,9 @@ func (t *Tunnel) waitForPodReady(ctx context.Context, podName string) error { } }, }) + if err != nil { + return errors.Trace(err) + } go informer.Run(stopChan) diff --git a/cloud/clouds.go b/cloud/clouds.go index 23e112e35ad..b7ae8466ab5 100644 --- a/cloud/clouds.go +++ b/cloud/clouds.go @@ -259,7 +259,7 @@ func (r Region) IsEmpty() bool { // unmarshalling. type cloudSet struct { // Clouds is a map of cloud definitions, keyed on cloud name. - Clouds map[string]*cloud `yaml:"clouds"` + Clouds map[string]cloud `yaml:"clouds"` } // cloud is equivalent to Cloud, for marshalling and unmarshalling. @@ -408,7 +408,7 @@ func PublicCloudMetadata(searchPath ...string) (result map[string]Cloud, fallbac // ParseOneCloud parses the given yaml bytes into a single Cloud metadata. func ParseOneCloud(data []byte) (Cloud, error) { - c := &cloud{} + var c cloud if err := yaml.Unmarshal(data, &c); err != nil { return Cloud{}, errors.Annotate(err, "cannot unmarshal yaml cloud metadata") } @@ -464,7 +464,7 @@ func ParseCloudMetadata(data []byte) (map[string]Cloud, error) { } } else { // Unable to coerce cloudSet, try to unmarshal into a map[string]*cloud - cloudMap := make(map[string]*cloud) + cloudMap := make(map[string]cloud) if errCloudMap := yaml.Unmarshal(data, &cloudMap); errCloudMap != nil { return nil, errors.Errorf("Invalid cloud metadata %s", yamlMap) } @@ -535,7 +535,7 @@ func IsSameCloudMetadata(meta1, meta2 map[string]Cloud) (bool, error) { // marshalCloudMetadata marshals the given clouds to YAML. func marshalCloudMetadata(cloudsMap map[string]Cloud) ([]byte, error) { - clouds := cloudSet{make(map[string]*cloud)} + clouds := cloudSet{make(map[string]cloud)} for name, metadata := range cloudsMap { clouds.Clouds[name] = cloudToInternal(metadata, false) } @@ -557,10 +557,10 @@ func UnmarshalCloud(in []byte) (Cloud, error) { if err := yaml.Unmarshal(in, &internal); err != nil { return Cloud{}, errors.Annotate(err, "cannot unmarshal yaml cloud metadata") } - return cloudFromInternal(&internal), nil + return cloudFromInternal(internal), nil } -func cloudToInternal(in Cloud, withName bool) *cloud { +func cloudToInternal(in Cloud, withName bool) cloud { var regions regions for _, r := range in.Regions { regions.Slice = append(regions.Slice, yaml.MapItem{ @@ -576,7 +576,7 @@ func cloudToInternal(in Cloud, withName bool) *cloud { if !withName { name = "" } - return &cloud{ + return cloud{ Name: name, Type: in.Type, HostCloudRegion: in.HostCloudRegion, @@ -593,7 +593,7 @@ func cloudToInternal(in Cloud, withName bool) *cloud { } } -func cloudFromInternal(in *cloud) Cloud { +func cloudFromInternal(in cloud) Cloud { var regions []Region if len(in.Regions.Map) > 0 { for _, item := range in.Regions.Slice { diff --git a/cloud/clouds_test.go b/cloud/clouds_test.go index 0589b38fcef..03ee5e13d9c 100644 --- a/cloud/clouds_test.go +++ b/cloud/clouds_test.go @@ -276,6 +276,19 @@ clouds: s.assertCompareClouds(c, metadata, false) } +func (s *cloudSuite) TestMalformedYAMLNoPanic(_ *gc.C) { + // Note the bad indentation. This case was reported under LP:2039322. + metadata := ` +clouds: +manual-cloud: + type: manual + endpoint: ubuntu@some-host-fqdn +`[1:] + + // We don't care about the result, just that there is no panic. + _, _ = cloud.ParseCloudMetadata([]byte(metadata)) +} + func (s *cloudSuite) TestRegionNames(c *gc.C) { regions := []cloud.Region{ {Name: "mars"}, diff --git a/cmd/containeragent/initialize/package_test.go b/cmd/containeragent/initialize/package_test.go index eab4d06dc70..585850655e1 100644 --- a/cmd/containeragent/initialize/package_test.go +++ b/cmd/containeragent/initialize/package_test.go @@ -50,6 +50,7 @@ func (*importSuite) TestImports(c *gc.C) { "core/constraints", "core/database", "core/devices", + "core/facades", "core/instance", "core/leadership", "core/lease", diff --git a/cmd/juju/application/integrate.go b/cmd/juju/application/integrate.go index b9070bcdbed..75b067b75f2 100644 --- a/cmd/juju/application/integrate.go +++ b/cmd/juju/application/integrate.go @@ -204,6 +204,7 @@ func (c *addRelationCommand) Info() *cmd.Info { "consume", "find-offers", "set-firewall-rule", + "suspend-relation", }, } return jujucmd.Info(addCmd) diff --git a/cmd/juju/charmhub/download.go b/cmd/juju/charmhub/download.go index 5531a9d1294..c1df5c35502 100644 --- a/cmd/juju/charmhub/download.go +++ b/cmd/juju/charmhub/download.go @@ -37,6 +37,12 @@ by a specified name. Downloading for a specific base can be done via --base. --base can be specified using the OS name and the version of the OS, separated by @. For example, --base ubuntu@22.04. +By default, the latest revision in the default channel will be +downloaded. To download the latest revision from another channel, +use --channel. To download a specific revision, use --revision, +which cannot be used together with --arch, --base, --channel or +--series. + Adding a hyphen as the second argument allows the download to be piped to stdout. ` @@ -61,6 +67,7 @@ type downloadCommand struct { channel string charmOrBundle string + revision int archivePath string pipeToStdout bool noProgress bool @@ -92,6 +99,7 @@ func (c *downloadCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.series, "series", SeriesAll, "specify a series. DEPRECATED use --base") f.StringVar(&c.base, "base", "", "specify a base") f.StringVar(&c.channel, "channel", "", "specify a channel to use instead of the default release") + f.IntVar(&c.revision, "revision", -1, "specify a revision of the charm to download") f.StringVar(&c.archivePath, "filepath", "", "filepath location of the charm to download to") f.BoolVar(&c.noProgress, "no-progress", false, "disable the progress bar") } @@ -103,6 +111,14 @@ func (c *downloadCommand) Init(args []string) error { return errors.New("--series and --base cannot be specified together") } + hasArch := c.arch != ArchAll && c.arch != "" + hasBase := c.base != "" + hasChannel := c.channel != "" + hasSeries := c.series != SeriesAll && c.series != "" + if c.revision != -1 && (hasArch || hasBase || hasChannel || hasSeries) { + return errors.New("--revision cannot be specified together with --arch, --base, --channel or --series") + } + if err := c.charmHubCommand.Init(args); err != nil { return errors.Trace(err) } @@ -193,7 +209,7 @@ func (c *downloadCommand) Run(cmdContext *cmd.Context) error { } pArch := c.arch - if pArch == "all" || pArch == "" { + if pArch == ArchAll || pArch == "" { pArch = arch.DefaultArchitecture } if base.Empty() { @@ -219,8 +235,13 @@ func (c *downloadCommand) Run(cmdContext *cmd.Context) error { path = fmt.Sprintf("%s_r%d.%s", entity.Name, entity.Revision, entityType) } - cmdContext.Infof("Fetching %s %q revision %d using %q channel and base %q", - entityType, entity.Name, entity.Revision, normChannel, normBase) + if c.revision == -1 { + cmdContext.Infof("Fetching %s %q revision %d using %q channel and base %q", + entityType, entity.Name, entity.Revision, normChannel, normBase) + } else { + cmdContext.Infof("Fetching %s %q revision %d", + entityType, entity.Name, entity.Revision) + } resourceURL, err := url.Parse(entity.Download.URL) if err != nil { @@ -282,13 +303,21 @@ func (c *downloadCommand) refresh( return nil, nil, errors.Trace(err) } - refreshConfig, err := charmhub.InstallOneFromChannel(c.charmOrBundle, normChannel.String(), charmhub.RefreshBase{ - Architecture: normBase.Architecture, - Name: normBase.OS, - Channel: normBase.Channel, - }) - if err != nil { - return nil, nil, errors.Trace(err) + var refreshConfig charmhub.RefreshConfig + if c.revision == -1 { + refreshConfig, err = charmhub.InstallOneFromChannel(c.charmOrBundle, normChannel.String(), charmhub.RefreshBase{ + Architecture: normBase.Architecture, + Name: normBase.OS, + Channel: normBase.Channel, + }) + if err != nil { + return nil, nil, errors.Trace(err) + } + } else { + refreshConfig, err = charmhub.InstallOneFromRevision(c.charmOrBundle, c.revision) + if err != nil { + return nil, nil, errors.Trace(err) + } } results, err := client.Refresh(ctx, refreshConfig) @@ -304,6 +333,9 @@ func (c *downloadCommand) refresh( for _, res := range results { if res.Error != nil { if res.Error.Code == transport.ErrorCodeRevisionNotFound { + if c.revision != -1 { + return nil, nil, errors.Errorf("unable to locate %s revison %d: %s", c.charmOrBundle, c.revision, res.Error.Message) + } possibleBases, err := c.suggested(cmdContext, base, normChannel.String(), res.Error.Extra.Releases) // The following will attempt to refresh the charm with the // suggested series. If it can't do that, it will give up after diff --git a/cmd/juju/charmhub/download_test.go b/cmd/juju/charmhub/download_test.go index e3813ecbbba..b4092e6def0 100644 --- a/cmd/juju/charmhub/download_test.go +++ b/cmd/juju/charmhub/download_test.go @@ -218,6 +218,69 @@ func (s *downloadSuite) TestRunWithInvalidStdout(c *gc.C) { c.Assert(err, gc.ErrorMatches, `expected a charm or bundle name, followed by hyphen to pipe to stdout`) } +func (s *downloadSuite) TestRunWithRevision(c *gc.C) { + defer s.setUpMocks(c).Finish() + + url := "http://example.org/" + + s.expectRefresh(url) + s.expectDownload(c, url) + s.expectFilesystem(c) + + command := &downloadCommand{ + charmHubCommand: s.newCharmHubCommand(), + } + command.SetFilesystem(s.filesystem) + err := cmdtesting.InitCommand(command, []string{"test", "--revision=123"}) + c.Assert(err, jc.ErrorIsNil) + + ctx := commandContextForTest(c) + err = command.Run(ctx) + c.Assert(err, jc.ErrorIsNil) + c.Assert(cmdtesting.Stderr(ctx), gc.Matches, "(?s)"+` +Fetching charm "test" revision 123 +Install the "test" charm with: + juju deploy ./test_r123\.charm +`[1:]) +} + +func (s *downloadSuite) TestRunWithRevisionNotFound(c *gc.C) { + defer s.setUpMocks(c).Finish() + + s.expectRefreshUnsupportedBase() + + command := &downloadCommand{ + charmHubCommand: s.newCharmHubCommand(), + } + command.SetFilesystem(s.filesystem) + err := cmdtesting.InitCommand(command, []string{"test", "--revision=99"}) + c.Assert(err, jc.ErrorIsNil) + + ctx := commandContextForTest(c) + err = command.Run(ctx) + c.Assert(err, gc.ErrorMatches, `unable to locate test revison 99: No revision was found in the Store.`) +} + +func (s *downloadSuite) TestRunWithRevisionAndOtherArgs(c *gc.C) { + defer s.setUpMocks(c).Finish() + + command := &downloadCommand{ + charmHubCommand: s.newCharmHubCommand(), + } + + err := cmdtesting.InitCommand(command, []string{"test", "--arch=amd64", "--revision=99"}) + c.Check(err, gc.ErrorMatches, `--revision cannot be specified together with --arch, --base, --channel or --series`) + + err = cmdtesting.InitCommand(command, []string{"test", "--base=ubuntu@22.04", "--revision=99"}) + c.Check(err, gc.ErrorMatches, `--revision cannot be specified together with --arch, --base, --channel or --series`) + + err = cmdtesting.InitCommand(command, []string{"test", "--channel=edge", "--revision=99"}) + c.Check(err, gc.ErrorMatches, `--revision cannot be specified together with --arch, --base, --channel or --series`) + + err = cmdtesting.InitCommand(command, []string{"test", "--series=jammy", "--revision=99"}) + c.Check(err, gc.ErrorMatches, `--revision cannot be specified together with --arch, --base, --channel or --series`) +} + func (s *downloadSuite) newCharmHubCommand() *charmHubCommand { return &charmHubCommand{ arches: arch.AllArches(), diff --git a/cmd/juju/commands/debuglog.go b/cmd/juju/commands/debuglog.go index 3fad8ad2d1e..85c7728e684 100644 --- a/cmd/juju/commands/debuglog.go +++ b/cmd/juju/commands/debuglog.go @@ -24,7 +24,6 @@ import ( "github.com/juju/juju/api/common" jujucmd "github.com/juju/juju/cmd" "github.com/juju/juju/cmd/modelcmd" - "github.com/juju/juju/core/model" "github.com/juju/juju/jujuclient" ) @@ -82,10 +81,6 @@ exit: juju debug-log --include mysql/0 --limit 50 -Include only messages from the gitlab-k8s application: - - juju debug-log --include gitlab-k8s - Show all messages from the apache/2 unit or machine 1 and then exit: juju debug-log --replay --include apache/2 --include machine-1 --no-tail @@ -218,18 +213,8 @@ func (c *debugLogCommand) Init(args []string) error { if c.ms { c.format = c.format + ".000" } - modelType, err := c.ModelType() - if err != nil { - return errors.Trace(err) - } - isCaas := modelType == model.CAAS - if isCaas { - c.params.IncludeEntity = transform.Slice(c.params.IncludeEntity, c.parseCAASEntity) - c.params.ExcludeEntity = transform.Slice(c.params.ExcludeEntity, c.parseCAASEntity) - } else { - c.params.IncludeEntity = transform.Slice(c.params.IncludeEntity, c.parseEntity) - c.params.ExcludeEntity = transform.Slice(c.params.ExcludeEntity, c.parseEntity) - } + c.params.IncludeEntity = transform.Slice(c.params.IncludeEntity, c.parseEntity) + c.params.ExcludeEntity = transform.Slice(c.params.ExcludeEntity, c.parseEntity) return cmd.CheckEmpty(args) } @@ -254,21 +239,6 @@ func (c *debugLogCommand) parseEntity(entity string) string { } } -func (c *debugLogCommand) parseCAASEntity(entity string) string { - tag, err := names.ParseTag(entity) - switch { - case strings.Contains(entity, "*"): - return entity - case err == nil && tag.Kind() == names.ApplicationTagKind: - return tag.String() - case names.IsValidApplication(entity): - return names.NewApplicationTag(entity).String() - default: - logger.Warningf("%q was not recognised as a valid application name. Only applications produce logs for CAAS models application", entity) - return entity - } -} - type DebugLogAPI interface { WatchDebugLog(params common.DebugLogParams) (<-chan common.LogMessage, error) Close() error diff --git a/cmd/juju/waitfor/errors.go b/cmd/juju/waitfor/errors.go index 64da295a81e..b0da24a6d73 100644 --- a/cmd/juju/waitfor/errors.go +++ b/cmd/juju/waitfor/errors.go @@ -102,8 +102,10 @@ func helpLineError(input string, pos query.Position) string { if leading+offset > len(input) { offset = leading - len(input) } - builder.WriteString(strings.Repeat("^", offset)) - builder.WriteString("\n") + if offset > 0 { + builder.WriteString(strings.Repeat("^", offset)) + builder.WriteString("\n") + } return builder.String() } diff --git a/core/charm/repository/charmhub.go b/core/charm/repository/charmhub.go index e47189ca791..1d416a6b023 100644 --- a/core/charm/repository/charmhub.go +++ b/core/charm/repository/charmhub.go @@ -110,6 +110,8 @@ func (c *CharmHubRepository) ResolveForDeploy(ctx context.Context, arg corecharm // re-request, but we end up with missing data and potential incorrect // charm downloads later. func (c *CharmHubRepository) resolveWithPreferredChannel(ctx context.Context, charmName string, requestedOrigin corecharm.Origin) (*charm.URL, corecharm.Origin, []corecharm.Platform, transport.RefreshResponse, error) { + c.logger.Tracef("Resolving CharmHub charm %q with origin %v", charmName, requestedOrigin) + // First attempt to find the charm based on the only input provided. response, err := c.refreshOne(ctx, charmName, requestedOrigin) if err != nil { @@ -272,7 +274,7 @@ func (c *CharmHubRepository) retryResolveWithPreferredChannel(ctx context.Contex ) switch resErr.Code { case transport.ErrorCodeInvalidCharmPlatform, transport.ErrorCodeInvalidCharmBase: - c.logger.Tracef("Invalid charm platform %q %v - Default Base: %v", charmName, origin, resErr.Extra.DefaultBases) + c.logger.Tracef("Invalid charm base %q %v - Default Base: %v", charmName, origin, resErr.Extra.DefaultBases) if bases, err = c.selectNextBases(resErr.Extra.DefaultBases, origin); err != nil { return nil, errors.Annotatef(err, "selecting next bases") @@ -281,9 +283,7 @@ func (c *CharmHubRepository) retryResolveWithPreferredChannel(ctx context.Contex case transport.ErrorCodeRevisionNotFound: c.logger.Tracef("Revision not found %q %v - Releases: %v", charmName, origin, resErr.Extra.Releases) - if bases, err = c.selectNextBasesFromReleases(resErr.Extra.Releases, origin); err != nil { - return nil, errors.Annotatef(err, "selecting releases") - } + return nil, errors.Annotatef(c.handleRevisionNotFound(resErr.Extra.Releases, origin), "selecting releases") default: return nil, errors.Errorf("resolving error: %s", resErr.Message) @@ -856,36 +856,37 @@ func (c *CharmHubRepository) selectNextBases(bases []transport.Base, origin core return results, nil } -func (c *CharmHubRepository) selectNextBasesFromReleases(releases []transport.Release, origin corecharm.Origin) ([]corecharm.Platform, error) { +func (c *CharmHubRepository) handleRevisionNotFound(releases []transport.Release, origin corecharm.Origin) error { if len(releases) == 0 { - return nil, errors.Errorf("no releases available") - } - if origin.Platform.Channel == "" { - // If the user passed in a branch, but not enough information about the - // arch and channel, then we can help by giving a better error message. - if origin.Channel != nil && origin.Channel.Branch != "" { - return nil, errors.Errorf("ambiguous arch and series with channel %q, specify both arch and series along with channel", origin.Channel.String()) - } - // If the origin is empty, then we want to help the user out - // by display a series of suggestions to try. - suggestions := c.composeSuggestions(releases, origin) - var s string - if len(suggestions) > 0 { - s = fmt.Sprintf("\navailable releases are:\n %v", strings.Join(suggestions, "\n ")) - } - var channelName string - if origin.Channel != nil { - channelName = origin.Channel.String() - } - return nil, errSelection{ - err: errors.Errorf( - "charm or bundle not found for channel %q, platform %q%s", - channelName, origin.Platform.String(), s), - } + return errors.Errorf("no releases available") + } + // If the user passed in a branch, but not enough information about the + // arch and channel, then we can help by giving a better error message. + if origin.Channel != nil && origin.Channel.Branch != "" { + return errors.Errorf("ambiguous arch and series with channel %q, specify both arch and series along with channel", origin.Channel.String()) + } + // Help the user out by creating a list of channel/base suggestions to try. + suggestions := c.composeSuggestions(releases, origin) + var s string + if len(suggestions) > 0 { + s = fmt.Sprintf("\navailable releases are:\n %v", strings.Join(suggestions, "\n ")) + } + // If the origin's channel is nil, one wasn't specified by the user, + // so we requested "stable", which indicates the charm's default channel. + // However, at the time we're writing this message, we do not know what + // the charm's default channel is. + var channelString string + if origin.Channel != nil { + channelString = fmt.Sprintf("for channel %q", origin.Channel.String()) + } else { + channelString = "in the charm's default channel" } - // From the suggestion list, go look up a release that we can retry. - return selectReleaseByArchAndChannel(releases, origin) + return errSelection{ + err: errors.Errorf( + "charm or bundle not found %s, base %q%s", + channelString, origin.Platform.String(), s), + } } type errSelection struct { @@ -992,6 +993,10 @@ func refreshConfig(charmName string, origin corecharm.Origin) (charmhub.RefreshC } func (c *CharmHubRepository) composeSuggestions(releases []transport.Release, origin corecharm.Origin) []string { + charmRisks := set.NewStrings() + for _, v := range charm.Risks { + charmRisks.Add(string(v)) + } channelSeries := make(map[string][]string) for _, release := range releases { arch := release.Base.Architecture @@ -1019,52 +1024,26 @@ func (c *CharmHubRepository) composeSuggestions(releases []transport.Release, or c.logger.Errorf("converting version to base: %s", err) continue } - channelSeries[release.Channel] = append(channelSeries[release.Channel], base.DisplayString()) - } - - var suggestions []string - // Sort for latest channels to be suggested first. - // Assumes that releases have normalized channels. - for _, r := range charm.Risks { - risk := string(r) - if values, ok := channelSeries[risk]; ok { - suggestions = append(suggestions, fmt.Sprintf("channel %q: available bases are: %s", risk, strings.Join(values, ", "))) - delete(channelSeries, risk) + // Now that we have default tracks other than latest: + // If a channel is risk only, add latest as the track + // to be more clear for the user facing error message. + // At this point, we do not know the default channel, + // or if the charm has one, therefore risk only output + // is ambiguous. + charmChannel := release.Channel + if charmRisks.Contains(charmChannel) { + charmChannel = "latest/" + charmChannel } + channelSeries[charmChannel] = append(channelSeries[charmChannel], base.DisplayString()) } + var suggestions []string for channel, values := range channelSeries { suggestions = append(suggestions, fmt.Sprintf("channel %q: available bases are: %s", channel, strings.Join(values, ", "))) } return suggestions } -func selectReleaseByArchAndChannel(releases []transport.Release, origin corecharm.Origin) ([]corecharm.Platform, error) { - var ( - empty = origin.Channel == nil - arch = origin.Platform.Architecture - channel charm.Channel - results []corecharm.Platform - ) - if !empty { - channel = origin.Channel.Normalize() - } - seen := set.NewStrings() - for _, release := range releases { - base := release.Base - - platform, err := corecharm.ParsePlatform(fmt.Sprintf("%s/%s/%s", arch, base.Name, base.Channel)) - if err != nil { - return nil, errors.Annotate(err, "base") - } - if !seen.Contains(platform.String()) && (empty || channel.String() == release.Channel) && (base.Architecture == "all" || base.Architecture == arch) { - seen.Add(platform.String()) - results = append(results, platform) - } - } - return results, nil -} - func resourceFromRevision(rev transport.ResourceRevision) (charmresource.Resource, error) { resType, err := charmresource.ParseType(rev.Type) if err != nil { diff --git a/core/charm/repository/charmhub_test.go b/core/charm/repository/charmhub_test.go index 34e36f7dec3..af0f77c161a 100644 --- a/core/charm/repository/charmhub_test.go +++ b/core/charm/repository/charmhub_test.go @@ -331,17 +331,15 @@ func (s *charmHubRepositorySuite) TestResolveRevisionNotFoundErrorWithNoSeries(c _, _, _, err := s.newClient().ResolveWithPreferredChannel(context.Background(), "wordpress", origin) c.Assert(err, gc.ErrorMatches, - `(?m)selecting releases: charm or bundle not found for channel "", platform "amd64" + `(?m)selecting releases: charm or bundle not found in the charm's default channel, base "amd64" available releases are: - channel "stable": available bases are: ubuntu@20.04`) + channel "latest/stable": available bases are: ubuntu@20.04`) } func (s *charmHubRepositorySuite) TestResolveRevisionNotFoundError(c *gc.C) { defer s.setupMocks(c).Finish() s.expectedRefreshRevisionNotFoundError(c) - s.expectCharmRefreshInstallOneFromChannel(c) - curl := charm.MustParseURL("ch:wordpress") origin := corecharm.Origin{ Source: "charm-hub", Platform: corecharm.Platform{ @@ -351,26 +349,12 @@ func (s *charmHubRepositorySuite) TestResolveRevisionNotFoundError(c *gc.C) { }, } - obtainedCurl, obtainedOrigin, obtainedBases, err := s.newClient().ResolveWithPreferredChannel(context.Background(), "wordpress", origin) - c.Assert(err, jc.ErrorIsNil) - - curl.Revision = 16 - - origin.Type = "charm" - origin.Revision = &curl.Revision - origin.Channel = &charm.Channel{ - Track: "latest", - Risk: "stable", - } - origin.Platform.Architecture = arch.DefaultArchitecture - origin.Platform.OS = "ubuntu" - origin.Platform.Channel = "20.04" - - expected := s.expectedCURL(curl, 16, arch.DefaultArchitecture, "focal") - - c.Assert(obtainedCurl, jc.DeepEquals, expected) - c.Assert(obtainedOrigin, jc.DeepEquals, origin) - c.Assert(obtainedBases, jc.SameContents, []corecharm.Platform{{OS: "ubuntu", Channel: "20.04", Architecture: "amd64"}}) + repo := NewCharmHubRepository(s.logger, s.client) + _, _, _, err := repo.ResolveWithPreferredChannel(context.Background(), "wordpress", origin) + c.Assert(err, gc.ErrorMatches, + `(?m)selecting releases: charm or bundle not found in the charm's default channel, base "amd64/ubuntu/18.04" +available releases are: + channel "latest/stable": available bases are: ubuntu@20.04`) } func (s *charmHubRepositorySuite) TestDownloadCharm(c *gc.C) { @@ -1228,7 +1212,7 @@ func (*selectNextBaseSuite) TestSelectNextBaseWithCentosBase(c *gc.C) { func (*selectNextBaseSuite) TestSelectNextBasesFromReleasesNoReleasesError(c *gc.C) { channel := corecharm.MustParseChannel("stable/foo") repo := new(CharmHubRepository) - _, err := repo.selectNextBasesFromReleases([]transport.Release{}, corecharm.Origin{ + err := repo.handleRevisionNotFound([]transport.Release{}, corecharm.Origin{ Channel: &channel, }) c.Assert(err, gc.ErrorMatches, `no releases available`) @@ -1237,7 +1221,7 @@ func (*selectNextBaseSuite) TestSelectNextBasesFromReleasesNoReleasesError(c *gc func (*selectNextBaseSuite) TestSelectNextBasesFromReleasesAmbiguousMatchError(c *gc.C) { channel := corecharm.MustParseChannel("stable/foo") repo := new(CharmHubRepository) - _, err := repo.selectNextBasesFromReleases([]transport.Release{ + err := repo.handleRevisionNotFound([]transport.Release{ {}, }, corecharm.Origin{ Channel: &channel, @@ -1250,7 +1234,7 @@ func (s *selectNextBaseSuite) TestSelectNextBasesFromReleasesSuggestionError(c * repo := NewCharmHubRepository(s.logger, nil) channel := corecharm.MustParseChannel("stable") - _, err := repo.selectNextBasesFromReleases([]transport.Release{{ + err := repo.handleRevisionNotFound([]transport.Release{{ Base: transport.Base{ Name: "os", Channel: "series", @@ -1260,14 +1244,13 @@ func (s *selectNextBaseSuite) TestSelectNextBasesFromReleasesSuggestionError(c * }}, corecharm.Origin{ Channel: &channel, }) - c.Assert(err, gc.ErrorMatches, `charm or bundle not found for channel "stable", platform ""`) + c.Assert(err, gc.ErrorMatches, `charm or bundle not found for channel "stable", base ""`) } func (s *selectNextBaseSuite) TestSelectNextBasesFromReleasesSuggestion(c *gc.C) { defer s.setupMocks(c).Finish() repo := NewCharmHubRepository(s.logger, nil) - - _, err := repo.selectNextBasesFromReleases([]transport.Release{{ + err := repo.handleRevisionNotFound([]transport.Release{{ Base: transport.Base{ Name: "ubuntu", Channel: "20.04", @@ -1280,9 +1263,9 @@ func (s *selectNextBaseSuite) TestSelectNextBasesFromReleasesSuggestion(c *gc.C) }, }) c.Assert(err, gc.ErrorMatches, - `charm or bundle not found for channel "", platform "arch" + `charm or bundle not found in the charm's default channel, base "arch" available releases are: - channel "stable": available bases are: ubuntu@20.04`) + channel "latest/stable": available bases are: ubuntu@20.04`) } func (s *selectNextBaseSuite) setupMocks(c *gc.C) *gomock.Controller { @@ -1337,7 +1320,7 @@ func (s *composeSuggestionsSuite) TestSuggestion(c *gc.C) { }, }) c.Assert(suggestions, gc.DeepEquals, []string{ - `channel "stable": available bases are: ubuntu@20.04`, + `channel "latest/stable": available bases are: ubuntu@20.04`, }) } @@ -1357,7 +1340,7 @@ func (s *composeSuggestionsSuite) TestSuggestionWithRisk(c *gc.C) { }, }) c.Assert(suggestions, gc.DeepEquals, []string{ - `channel "stable": available bases are: ubuntu@20.04`, + `channel "latest/stable": available bases are: ubuntu@20.04`, }) } @@ -1397,8 +1380,8 @@ func (s *composeSuggestionsSuite) TestMultipleSuggestion(c *gc.C) { Architecture: "c", }, }) - c.Assert(suggestions, gc.DeepEquals, []string{ - `channel "stable": available bases are: ubuntu@20.04, ubuntu@18.04`, + c.Assert(suggestions, jc.SameContents, []string{ + `channel "latest/stable": available bases are: ubuntu@20.04, ubuntu@18.04`, `channel "2.0/stable": available bases are: ubuntu@18.04`, }) } @@ -1419,7 +1402,7 @@ func (s *composeSuggestionsSuite) TestCentosSuggestion(c *gc.C) { }, }) c.Assert(suggestions, gc.DeepEquals, []string{ - `channel "stable": available bases are: centos@7`, + `channel "latest/stable": available bases are: centos@7`, }) } @@ -1430,207 +1413,3 @@ func (s *composeSuggestionsSuite) setupMocks(c *gc.C) *gomock.Controller { s.logger.EXPECT().Tracef(gomock.Any(), gomock.Any()).AnyTimes() return ctrl } - -type selectReleaseByChannelSuite struct { - testing.IsolationSuite -} - -var _ = gc.Suite(&selectReleaseByChannelSuite{}) - -func (selectReleaseByChannelSuite) TestNoReleases(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{}, corecharm.Origin{}) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform(nil)) -} - -func (selectReleaseByChannelSuite) TestSelection(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "os", - Channel: "20.04", - Architecture: "arch", - }, - Channel: "stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "arch", - }, - Channel: &charm.Channel{ - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "arch", - OS: "os", - Channel: "20.04", - }}) -} - -func (selectReleaseByChannelSuite) TestSelectionSeriesInRelease(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "ubuntu", - Channel: "focal", - Architecture: "arch", - }, - Channel: "stable", - }, { - Base: transport.Base{ - Name: "ubuntu", - Channel: "20.04", - Architecture: "arch", - }, - Channel: "stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "arch", - }, - Channel: &charm.Channel{ - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "arch", - OS: "ubuntu", - Channel: "20.04", - }}) -} - -func (selectReleaseByChannelSuite) TestSelectionWithCentos(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "centos", - Channel: "7", - Architecture: "arch", - }, - Channel: "stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "arch", - }, - Channel: &charm.Channel{ - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "arch", - OS: "centos", - Channel: "7", - }}) -} - -func (selectReleaseByChannelSuite) TestAllSelection(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "os", - Channel: "16.04", - Architecture: "all", - }, - Channel: "stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "arch", - }, - Channel: &charm.Channel{ - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "arch", - OS: "os", - Channel: "16.04", - }}) -} - -func (selectReleaseByChannelSuite) TestMultipleSelectionMultipleReturned(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "a", - Channel: "14.04", - Architecture: "c", - }, - Channel: "1.0/edge", - }, { - Base: transport.Base{ - Name: "d", - Channel: "16.04", - Architecture: "all", - }, - Channel: "2.0/stable", - }, { - Base: transport.Base{ - Name: "f", - Channel: "18.04", - Architecture: "h", - }, - Channel: "3.0/stable", - }, { - Base: transport.Base{ - Name: "g", - Channel: "20.04", - Architecture: "h", - }, - Channel: "3.0/stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "h", - }, - Channel: &charm.Channel{ - Track: "3.0", - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "h", - OS: "f", - Channel: "18.04", - }, { - Architecture: "h", - OS: "g", - Channel: "20.04", - }}) -} - -func (selectReleaseByChannelSuite) TestMultipleSelection(c *gc.C) { - release, err := selectReleaseByArchAndChannel([]transport.Release{{ - Base: transport.Base{ - Name: "a", - Channel: "14.04", - Architecture: "c", - }, - Channel: "1.0/edge", - }, { - Base: transport.Base{ - Name: "d", - Channel: "16.04", - Architecture: "all", - }, - Channel: "2.0/stable", - }, { - Base: transport.Base{ - Name: "f", - Channel: "18.04", - Architecture: "h", - }, - Channel: "3.0/stable", - }}, corecharm.Origin{ - Platform: corecharm.Platform{ - Architecture: "h", - }, - Channel: &charm.Channel{ - Track: "3.0", - Risk: "stable", - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(release, gc.DeepEquals, []corecharm.Platform{{ - Architecture: "h", - OS: "f", - Channel: "18.04", - }}) -} diff --git a/core/facades/facade.go b/core/facades/facade.go new file mode 100644 index 00000000000..f704bc56cb6 --- /dev/null +++ b/core/facades/facade.go @@ -0,0 +1,59 @@ +// Copyright 2023 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package facades + +import ( + "github.com/juju/collections/set" +) + +// FacadeVersion is a list of version numbers for a single facade. +type FacadeVersion []int + +// NamedFacadeVersion is a map of facade name to version numbers. +type NamedFacadeVersion struct { + Name string + Versions FacadeVersion +} + +// FacadeVersions is a map of facade name to version numbers. The facade version +// numbers contain each version of the facade that the API server is capable of +// supporting. This supports having holes in the version numbers, so that we can +// depreciate broken versions of the facade. +type FacadeVersions map[string]FacadeVersion + +// Merge adds the other facade versions to the current facade versions. +func (f FacadeVersions) Add(others ...NamedFacadeVersion) FacadeVersions { + for _, other := range others { + f[other.Name] = set.NewInts(f[other.Name]...).Union(set.NewInts(other.Versions...)).SortedValues() + } + return f +} + +// BestVersion finds the newest version in the version list that we can +// use. +func BestVersion(desired FacadeVersion, versions FacadeVersion) int { + intersection := set.NewInts(desired...).Intersection(set.NewInts(versions...)) + if intersection.Size() == 0 { + return 0 + } + sorted := intersection.SortedValues() + return sorted[len(sorted)-1] +} + +// CompleteIntersection returns true if the src and dest facades have a +// complete intersection. This means that the dest facades support all of +// the src facades. +// src is the facades that are required, dest is the full set of facades +// that are supported. +func CompleteIntersection(src, dest FacadeVersions) bool { + for name, versions := range src { + if _, ok := dest[name]; !ok { + return false + } + if BestVersion(versions, dest[name]) == 0 { + return false + } + } + return true +} diff --git a/core/facades/facade_test.go b/core/facades/facade_test.go new file mode 100644 index 00000000000..5032cba2113 --- /dev/null +++ b/core/facades/facade_test.go @@ -0,0 +1,106 @@ +// Copyright 2023 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package facades + +import ( + gc "gopkg.in/check.v1" + + "github.com/juju/juju/testing" +) + +type FacadeSuite struct { + testing.BaseSuite +} + +var _ = gc.Suite(&FacadeSuite{}) + +func (s *FacadeSuite) TestBestVersion(c *gc.C) { + tests := []struct { + versions FacadeVersion + desired FacadeVersion + expected int + }{{ + versions: FacadeVersion{1, 2, 3}, + desired: FacadeVersion{1}, + expected: 1, + }, { + versions: FacadeVersion{1, 2, 3}, + desired: FacadeVersion{1, 2}, + expected: 2, + }, { + versions: FacadeVersion{1, 2, 3}, + desired: FacadeVersion{1, 2, 3}, + expected: 3, + }, { + versions: FacadeVersion{}, + desired: FacadeVersion{0, 1, 2}, + expected: 0, + }} + for i, test := range tests { + c.Logf("test %d", i) + c.Check(BestVersion(test.desired, test.versions), gc.Equals, test.expected) + } +} + +func (s *FacadeSuite) TestCompleteIntersection(c *gc.C) { + tests := []struct { + src FacadeVersions + dst FacadeVersions + expected bool + }{{ + src: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + }, + expected: true, + }, { + src: FacadeVersions{ + "bar": FacadeVersion{1, 2, 3}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + }, + expected: false, + }, { + src: FacadeVersions{ + "foo": FacadeVersion{3, 4, 5}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + }, + expected: true, + }, { + src: FacadeVersions{ + "foo": FacadeVersion{4, 5}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + }, + expected: false, + }, { + src: FacadeVersions{ + "foo": FacadeVersion{2, 3, 4}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1}, + }, + expected: false, + }, { + src: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + "bar": FacadeVersion{3}, + }, + dst: FacadeVersions{ + "foo": FacadeVersion{1, 2, 3}, + "bar": FacadeVersion{1, 3}, + }, + expected: true, + }} + for i, test := range tests { + c.Logf("test %d", i) + c.Check(CompleteIntersection(test.src, test.dst), gc.Equals, test.expected) + } +} diff --git a/core/facades/package_test.go b/core/facades/package_test.go new file mode 100644 index 00000000000..87166c5f86e --- /dev/null +++ b/core/facades/package_test.go @@ -0,0 +1,14 @@ +// Copyright 2023 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package facades + +import ( + "testing" + + gc "gopkg.in/check.v1" +) + +func TestPackage(t *testing.T) { + gc.TestingT(t) +} diff --git a/go.mod b/go.mod index 373a7c59042..3bebf28027a 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/juju/juju go 1.21 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.2.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 github.com/EvilSuperstars/go-cidrman v0.0.0-20170211231153-4e5a4a63d9b7 github.com/aws/aws-sdk-go-v2 v1.21.0 github.com/aws/aws-sdk-go-v2/config v1.18.35 @@ -20,7 +20,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ecr v1.19.2 github.com/aws/aws-sdk-go-v2/service/iam v1.22.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3 - github.com/aws/smithy-go v1.14.2 + github.com/aws/smithy-go v1.17.0 github.com/bmizerany/pat v0.0.0-20160217103242-c068ca2f0aac github.com/canonical/go-dqlite v1.21.0 github.com/canonical/lxd v0.0.0-20230712132802-8d2a42545fd0 @@ -28,16 +28,16 @@ require ( github.com/canonical/sqlair v0.0.0-20230707154306-6f89ebb4ac5c github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/coreos/go-systemd/v22 v22.3.2 - github.com/docker/distribution v2.8.2+incompatible + github.com/docker/distribution v2.8.3+incompatible github.com/dustin/go-humanize v1.0.1 github.com/fsnotify/fsnotify v1.6.0 github.com/go-goose/goose/v5 v5.0.0-20230421180421-abaee9096e3a - github.com/go-logr/logr v1.2.4 + github.com/go-logr/logr v1.3.0 github.com/go-macaroon-bakery/macaroon-bakery/v3 v3.0.1 github.com/gofrs/uuid v4.2.0+incompatible + github.com/google/gnostic-models v0.6.8 github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.3.0 - github.com/googleapis/gnostic v0.5.5 + github.com/google/uuid v1.4.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/gorilla/websocket v1.5.0 @@ -89,9 +89,9 @@ require ( github.com/lestrrat-go/jwx/v2 v2.0.11 github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-sqlite3 v1.14.17 - github.com/microsoft/kiota-abstractions-go v1.2.0 - github.com/microsoft/kiota-http-go v1.0.1 - github.com/microsoftgraph/msgraph-sdk-go v1.14.0 + github.com/microsoft/kiota-abstractions-go v1.5.3 + github.com/microsoft/kiota-http-go v1.1.1 + github.com/microsoftgraph/msgraph-sdk-go v1.25.0 github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb github.com/mitchellh/mapstructure v1.5.0 github.com/mittwald/vaultgo v0.1.1 @@ -104,19 +104,19 @@ require ( github.com/rs/xid v1.5.0 github.com/vishvananda/netlink v1.2.1-beta.2 github.com/vmware/govmomi v0.21.1-0.20191008161538-40aebf13ba45 - go.opentelemetry.io/otel v1.16.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 - go.opentelemetry.io/otel/sdk v1.16.0 - go.opentelemetry.io/otel/trace v1.16.0 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 + go.opentelemetry.io/otel/sdk v1.21.0 + go.opentelemetry.io/otel/trace v1.21.0 go.uber.org/mock v0.2.0 - golang.org/x/crypto v0.14.0 - golang.org/x/net v0.17.0 - golang.org/x/oauth2 v0.13.0 - golang.org/x/sync v0.4.0 - golang.org/x/sys v0.13.0 - golang.org/x/tools v0.14.0 - google.golang.org/api v0.126.0 + golang.org/x/crypto v0.16.0 + golang.org/x/net v0.19.0 + golang.org/x/oauth2 v0.15.0 + golang.org/x/sync v0.5.0 + golang.org/x/sys v0.15.0 + golang.org/x/tools v0.16.0 + google.golang.org/api v0.152.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/httprequest.v1 v1.2.1 gopkg.in/ini.v1 v1.67.0 @@ -127,21 +127,21 @@ require ( gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.23.4 + k8s.io/api v0.28.4 k8s.io/apiextensions-apiserver v0.21.10 - k8s.io/apimachinery v0.23.4 - k8s.io/client-go v0.23.4 - k8s.io/klog/v2 v2.80.1 - k8s.io/utils v0.0.0-20230711102312-30195339c3c7 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/klog/v2 v2.110.1 + k8s.io/utils v0.0.0-20231127182322-b307cd553661 ) require ( - cloud.google.com/go/compute v1.20.1 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect github.com/Rican7/retry v0.3.1 // indirect github.com/adrg/xdg v0.3.3 // indirect github.com/armon/go-metrics v0.4.0 // indirect @@ -150,7 +150,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.41 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 // indirect @@ -169,6 +169,8 @@ require ( github.com/creack/pty v1.1.15 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect @@ -176,23 +178,26 @@ require ( github.com/gdamore/tcell/v2 v2.5.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.0.4 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/renameio v1.0.1 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.0 // indirect @@ -211,8 +216,9 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/sdk v0.5.1 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.11 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/go4 v0.0.0-20160222163258-40d72ab9641a // indirect github.com/juju/gojsonpointer v0.0.0-20150204194629-afe8b77aa08f // indirect @@ -239,12 +245,14 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lunixbochs/vtclean v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/microsoft/kiota-authentication-azure-go v1.0.0 // indirect github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect github.com/microsoft/kiota-serialization-json-go v1.0.4 // indirect + github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect github.com/microsoft/kiota-serialization-text-go v1.0.0 // indirect github.com/microsoftgraph/msgraph-sdk-go-core v1.0.0 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect @@ -257,9 +265,9 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/muhlemmer/gu v0.3.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect @@ -274,7 +282,7 @@ require ( github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect @@ -285,42 +293,34 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect + github.com/std-uritemplate/std-uritemplate/go v0.0.47 // indirect github.com/stretchr/testify v1.8.4 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/vishvananda/netns v0.0.4 // indirect - github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect github.com/zitadel/oidc/v2 v2.6.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/grpc v1.56.3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/gobwas/glob.v0 v0.2.3 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect - sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) -// This is copied from the go.mod file in github.com/canonical/lxd -// It is needed to avoid this error when running go list -m -// go: google.golang.org/grpc/naming@v0.0.0-00010101000000-000000000000: invalid version: unknown revision 000000000000 -replace google.golang.org/grpc/naming => google.golang.org/grpc v1.29.1 - replace gopkg.in/yaml.v2 => github.com/juju/yaml/v2 v2.0.0 diff --git a/go.sum b/go.sum index a095da6dd3c..8670f052dd4 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -47,34 +47,36 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.1 h1:kRt6idL93W/nYRkUPbZ81yxJeLFevvrLYkyJEVzLpYM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.1/go.mod h1:nPsyC5G3IY+ljp+OHp8w/xa9UuLWe7ehFADNkqCSTaw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 h1:qiir/pptnHqp6hV8QwV+IExYIf6cPsXBfUDUXQ27t2Y= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2/go.mod h1:jVRrRDLCOuif95HDYC23ADTMlvahB7tMdl519m9Iyjc= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 h1:/Di3vB4sNeQ+7A8efjUVENvyB945Wruvstucqp7ZArg= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0/go.mod h1:gM3K25LQlsET3QR+4V74zxCsFAy0r6xMNN9n80SZn+4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0 h1:xxe4naFUPYEW1W6C8yWrfFNmyZLnEbO+CsbsSF83wDo= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0/go.mod h1:aLFjumYDvv63tH1qnqkcmdjdZ6Sn+/viPv7H3jft0oY= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.2.0 h1:8d4U82r7ItT1Es91x3eUcAQweih36KWvUha8AZ9X0Rs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.2.0/go.mod h1:/1bkGperHinQbAHMWivoec/Ucu6//iXo6jn5mhmqCVU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 h1:HlZMUZW8S4P9oob1nCHxCCKrytxyLc+24nUJGssoEto= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0/go.mod h1:StGsLbuJh06Bd8IBfnAlIFV3fLb+gkczONWf15hpX2E= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 h1:Pmy0+3ox1IC3sp6musv87BFPIdQbqyPFjn7I8I0o2Js= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0/go.mod h1:ThfyMjs6auYrWPnYJjI3H4H++oVPrz01pizpu8lfl3A= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -82,19 +84,16 @@ github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -147,6 +146,8 @@ github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -179,9 +180,8 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.33/go.mod h1:S/zgOphghZA github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.34/go.mod h1:RZP0scceAyhMIQ9JvFp7HvkpcgqjL4l/4C+7RAeGbuM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.41 h1:EcSFdpLdkF3FWizimox0qYLuorn9e4PNMR27mvshGLs= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.41/go.mod h1:mKxUXW+TuwpCKKHVlmHGVVuBi9y9LKW8AiQodg23M5E= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20 h1:YIvKIfPXQVp0EhXUV644kmQo6cQPPSRmC44A1HSoJeg= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw= github.com/aws/aws-sdk-go-v2/service/ec2 v1.113.0 h1:r6pW/VOm8ea4GDEmwDwN2IkgYmu8JjcYzYvHJRs5sEw= @@ -214,8 +214,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvT github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI= +github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -276,11 +277,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= @@ -387,14 +383,16 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -413,18 +411,17 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -437,7 +434,6 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu48jqMdaOR/IZ4vdtJFuaFV8MpIE= github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= github.com/frankban/quicktest v1.1.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= @@ -458,7 +454,6 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I= github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -479,10 +474,9 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-macaroon-bakery/macaroon-bakery/v3 v3.0.1 h1:uvQJoKTHrFFu8zxoaopNKedRzwdy3+8H72we4T/5cGs= @@ -491,17 +485,24 @@ github.com/go-macaroon-bakery/macaroonpb v1.0.0 h1:It9exBaRMZ9iix1iJ6gwzfwsDE6Ex github.com/go-macaroon-bakery/macaroonpb v1.0.0/go.mod h1:UzrGOcbiwTXISFP2XDLDPjfhMINZa+fX/7A2lMd31zc= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= @@ -528,12 +529,11 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -575,7 +575,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -590,13 +591,14 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -612,28 +614,27 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v0.0.0-20170306145142-6a5e28554805/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -659,8 +660,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -749,9 +750,8 @@ github.com/im7mortal/kmutex v1.0.1/go.mod h1:f71c/Ugk/+58OHRAgvgzPP3QEiWGUjK13fd github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= @@ -773,6 +773,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1004,6 +1006,8 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/masterzen/azure-sdk-for-go v3.2.0-beta.0.20161014135628-ee4f0065d00c+incompatible/go.mod h1:mf8fjOu33zCqxUjuiU3I8S1lJMyEAlH+0F2+M5xl3hE= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -1043,20 +1047,22 @@ github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/microsoft/kiota-abstractions-go v1.2.0 h1:lUriJgqdCY/QajwWQOgTCQE9Atywfe2NHhgoTCSXTRE= -github.com/microsoft/kiota-abstractions-go v1.2.0/go.mod h1:RkxyZ5x87Njik7iVeQY9M2wtrrL1MJZcXiI/BxD/82g= +github.com/microsoft/kiota-abstractions-go v1.5.3 h1:qUTwuXCbMi99EkHaTh5NGMK5MOKxJn7u/M2FbYcesLY= +github.com/microsoft/kiota-abstractions-go v1.5.3/go.mod h1:xyBzTVCYrp7QBW4/p+RFi44PHwp/IPn2dZepuV4nF80= github.com/microsoft/kiota-authentication-azure-go v1.0.0 h1:29FNZZ/4nnCOwFcGWlB/sxPvWz487HA2bXH8jR5k2Rk= github.com/microsoft/kiota-authentication-azure-go v1.0.0/go.mod h1:rnx3PRlkGdXDcA/0lZQTbBwyYGmc+3POt7HpE/e4jGw= -github.com/microsoft/kiota-http-go v1.0.1 h1:818u3aiLpxj35hZgfUSqphQ18IUTK3gVdTE4cQ5vjLw= -github.com/microsoft/kiota-http-go v1.0.1/go.mod h1:H0cg+ly+5ZSR8z4swj5ea9O/GB5ll2YuYeQ0/pJs7AY= +github.com/microsoft/kiota-http-go v1.1.1 h1:W4Olo7Z/MwNZCfkcvH/5eLhnn7koRBMMRhLEnf5MPKo= +github.com/microsoft/kiota-http-go v1.1.1/go.mod h1:QzhhfW5xkoUuT+/ohflpHJvumWeXIxa/Xl0GmQ2M6mY= github.com/microsoft/kiota-serialization-form-go v1.0.0 h1:UNdrkMnLFqUCccQZerKjblsyVgifS11b3WCx+eFEsAI= github.com/microsoft/kiota-serialization-form-go v1.0.0/go.mod h1:h4mQOO6KVTNciMF6azi1J9QB19ujSw3ULKcSNyXXOMA= github.com/microsoft/kiota-serialization-json-go v1.0.4 h1:5TaISWwd2Me8clrK7SqNATo0tv9seOq59y4I5953egQ= github.com/microsoft/kiota-serialization-json-go v1.0.4/go.mod h1:rM4+FsAY+9AEpBsBzkFFis+b/LZLlNKKewuLwK9Q6Mg= +github.com/microsoft/kiota-serialization-multipart-go v1.0.0 h1:3O5sb5Zj+moLBiJympbXNaeV07K0d46IfuEd5v9+pBs= +github.com/microsoft/kiota-serialization-multipart-go v1.0.0/go.mod h1:yauLeBTpANk4L03XD985akNysG24SnRJGaveZf+p4so= github.com/microsoft/kiota-serialization-text-go v1.0.0 h1:XOaRhAXy+g8ZVpcq7x7a0jlETWnWrEum0RhmbYrTFnA= github.com/microsoft/kiota-serialization-text-go v1.0.0/go.mod h1:sM1/C6ecnQ7IquQOGUrUldaO5wj+9+v7G2W3sQ3fy6M= -github.com/microsoftgraph/msgraph-sdk-go v1.14.0 h1:YdhMvzu8bXcfIQGRur6NkXnv4cPOsMBJ44XjfWLOt9Y= -github.com/microsoftgraph/msgraph-sdk-go v1.14.0/go.mod h1:ccLv84FJFtwdSzYWM/HlTes5FLzkzzBsYh9kg93/WS8= +github.com/microsoftgraph/msgraph-sdk-go v1.25.0 h1:AKHYi6TqyXrKrbgYnVq4RNbxM9FHs+0McoFTmy9VH28= +github.com/microsoftgraph/msgraph-sdk-go v1.25.0/go.mod h1:9atTwyb9mpNq5MgTBPs2KAlDzGcMcTszDVzE4GeXCeo= github.com/microsoftgraph/msgraph-sdk-go-core v1.0.0 h1:7NWTfyXvOjoizW7PmxNp3+8wCKPgpODs/D1cUZ3fkAY= github.com/microsoftgraph/msgraph-sdk-go-core v1.0.0/go.mod h1:tQb4q3YMIj2dWhhXhQSJ4ELpol931ANKzHSYK5kX1qE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -1116,6 +1122,7 @@ github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2 github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1123,8 +1130,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1134,16 +1139,16 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1151,9 +1156,8 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -1262,8 +1266,8 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= @@ -1325,8 +1329,9 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/std-uritemplate/std-uritemplate/go v0.0.47 h1:erzz/DR4sOzWr0ca2MgSTkMckpLEsDySaTZwVFQq9zw= +github.com/std-uritemplate/std-uritemplate/go v0.0.47/go.mod h1:Qov4Ay4U83j37XjgxMYevGJFLbnZ2o9cEOhGufBKgKY= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1378,12 +1383,10 @@ github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9o github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +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/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= @@ -1391,8 +1394,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI= -github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= -github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1421,31 +1422,28 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= -go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1477,14 +1475,12 @@ golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1525,8 +1521,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1565,7 +1561,6 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -1588,8 +1583,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1602,10 +1597,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1619,8 +1612,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1648,7 +1641,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1676,7 +1668,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1717,7 +1708,6 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1730,18 +1720,17 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -1751,11 +1740,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1763,9 +1751,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1825,17 +1812,14 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1859,8 +1843,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1904,7 +1888,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1918,13 +1901,12 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 h1:s5YSX+ZH5b5vS9rnpGymvIyMpLRJizowqDlOuyjXnTk= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1952,11 +1934,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1969,7 +1948,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= @@ -2029,7 +2007,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs= gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -2047,20 +2024,20 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.21.10/go.mod h1:5kqv2pCXwcrOvV12WhVAtLZUKaM0kyrZ6nHObw8SojA= -k8s.io/api v0.23.4 h1:85gnfXQOWbJa1SiWGpE9EEtHs0UVvDyIsSMpEtl2D4E= -k8s.io/api v0.23.4/go.mod h1:i77F4JfyNNrhOjZF7OwwNJS5Y1S9dpwvb9iYRYRczfI= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= k8s.io/apiextensions-apiserver v0.21.10 h1:61ymf3Yw6dadgfUbWCEVud5j6l9rme8ocy6jJbuFK04= k8s.io/apiextensions-apiserver v0.21.10/go.mod h1:Uu9eBo+d489/K5pauF1oLaVxQzgvdng6aIb2LRlT/w8= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.21.10/go.mod h1:USs+ifLG6ZUgHGA/9lGxjdHzCB3hUO3fG1VBOwi0IHo= -k8s.io/apimachinery v0.23.4 h1:fhnuMd/xUL3Cjfl64j5ULKZ1/J9n8NuQEgNL+WXWfdM= -k8s.io/apimachinery v0.23.4/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.21.10/go.mod h1:dMEmFJ//OIDnnWmjcpVq+XkKQubRY1rAm5as7MwbIWQ= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.21.10/go.mod h1:nAGhVCjwhbDP2whk65n3STSCn24H/VGp1pKSk9UszU8= -k8s.io/client-go v0.23.4 h1:YVWvPeerA2gpUudLelvsolzH7c2sFoXXR5wM/sWqNFU= -k8s.io/client-go v0.23.4/go.mod h1:PKnIL4pqLuvYUK1WU7RLTMYKPiIh7MYShLshtRY9cj0= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= k8s.io/code-generator v0.21.10/go.mod h1:FbZCzn44pBTAjY3tIvLIQcF2514rGUzMP1liffRr5jQ= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.21.10/go.mod h1:zO/BjKH/RR6DoZEQAws7m+Q81pYZ+f5vDfujDqvWdlA= @@ -2068,25 +2045,21 @@ k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20211110012726-3cc51fd1e909/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210521133846-da695404a2bc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= +k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= launchpad.net/xmlpath v0.0.0-20130614043138-000000000004/go.mod h1:vqyExLOM3qBx7mvYRkoxjSCF945s0mbe7YynlKYXtsA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -2094,11 +2067,13 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.27/go.mod h1:tq2nT0Kx7W+/f2JVE+zxYtUhdjuELJkVpNz+x/QN5R4= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/docker/auth.go b/internal/docker/auth.go index 85be1ed8bfc..ee9ec94b4a6 100644 --- a/internal/docker/auth.go +++ b/internal/docker/auth.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "reflect" + "strings" "time" "github.com/docker/distribution/reference" @@ -174,6 +175,7 @@ func (rid ImageRepoDetails) SecretData() ([]byte, error) { if rid.BasicAuthConfig.Empty() && rid.TokenAuthConfig.Empty() { return nil, nil } + repo := strings.Split(rid.Repository, "/")[0] rid.Repository = "" if !rid.BasicAuthConfig.Empty() && rid.BasicAuthConfig.Auth.Empty() { rid.BasicAuthConfig.Auth = NewToken( @@ -181,7 +183,7 @@ func (rid ImageRepoDetails) SecretData() ([]byte, error) { } o := dockerConfigData{ Auths: map[string]ImageRepoDetails{ - rid.ServerAddress: rid, + repo: rid, }, } return json.Marshal(o) diff --git a/internal/docker/auth_test.go b/internal/docker/auth_test.go index 590769c8e2a..4bae66d8f0b 100644 --- a/internal/docker/auth_test.go +++ b/internal/docker/auth_test.go @@ -113,7 +113,7 @@ func (s *authSuite) TestValidateImageRepoDetails(c *gc.C) { func (s *authSuite) TestSecretData(c *gc.C) { imageRepoDetails := docker.ImageRepoDetails{ - Repository: "test-account", + Repository: "quay.io/test-account", ServerAddress: "quay.io", BasicAuthConfig: docker.BasicAuthConfig{ Auth: docker.NewToken("xxxxx=="), diff --git a/internal/docker/registry/internal/base_client.go b/internal/docker/registry/internal/base_client.go index ba141f8d963..568f90fc8d3 100644 --- a/internal/docker/registry/internal/base_client.go +++ b/internal/docker/registry/internal/base_client.go @@ -120,8 +120,8 @@ func transportCommon(transport http.RoundTripper, repoDetails *docker.ImageRepoD ), ) } - return newTokenTransport( - transport, repoDetails.Username, repoDetails.Password, repoDetails.Auth.Content(), "", false, + return newChallengeTransport( + transport, repoDetails.Username, repoDetails.Password, repoDetails.Auth.Content(), ), nil } diff --git a/internal/docker/registry/internal/package_test.go b/internal/docker/registry/internal/package_test.go index 74422ae866d..666e67fcafd 100644 --- a/internal/docker/registry/internal/package_test.go +++ b/internal/docker/registry/internal/package_test.go @@ -29,6 +29,7 @@ type ( var ( NewErrorTransport = newErrorTransport + NewChallengeTransport = newChallengeTransport NewBasicTransport = newBasicTransport NewTokenTransport = newTokenTransport NewElasticContainerRegistryForTest = newElasticContainerRegistryForTest diff --git a/internal/docker/registry/internal/transports.go b/internal/docker/registry/internal/transports.go index ab7866b99e1..464431d1cf9 100644 --- a/internal/docker/registry/internal/transports.go +++ b/internal/docker/registry/internal/transports.go @@ -27,6 +27,78 @@ func (f dynamicTransportFunc) RoundTrip(req *http.Request) (*http.Response, erro return transport.RoundTrip(req) } +type challengeTransport struct { + baseTransport http.RoundTripper + currentTransport http.RoundTripper + + username string + password string + authToken string +} + +func newChallengeTransport( + transport http.RoundTripper, username string, password string, authToken string, +) http.RoundTripper { + return &challengeTransport{ + baseTransport: transport, + username: username, + password: password, + authToken: authToken, + } +} + +func (t *challengeTransport) RoundTrip(req *http.Request) (*http.Response, error) { + transport := t.baseTransport + if t.currentTransport != nil { + transport = t.currentTransport + } + resp, err := transport.RoundTrip(req) + if err != nil { + return nil, errors.Trace(err) + } + originalResp := resp + if !isUnauthorizedResponse(originalResp) { + return resp, nil + } + for _, c := range challenge.ResponseChallenges(originalResp) { + if err != nil { + logger.Warningf("authentication failed: %s", err.Error()) + err = nil + } + switch strings.ToLower(c.Scheme) { + case "bearer": + tokenTransport := &tokenTransport{ + transport: t.baseTransport, + username: t.password, + password: t.password, + authToken: t.authToken, + } + err = tokenTransport.refreshOAuthToken(originalResp) + if err != nil { + continue + } + transport = tokenTransport + case "basic": + transport = newBasicTransport(t.baseTransport, t.username, t.password, t.authToken) + default: + err = fmt.Errorf("unknown WWW-Authenticate challenge scheme: %s", c.Scheme) + continue + } + resp, err = transport.RoundTrip(req) + if err == nil && !isUnauthorizedResponse(resp) { + t.currentTransport = transport + return resp, nil + } + } + if err != nil { + return nil, errors.Trace(err) + } + if t.password == "" && t.authToken == "" { + return nil, errors.NewUnauthorized(err, "authorization is required for a private registry") + } + return resp, nil +} + type basicTransport struct { transport http.RoundTripper username string @@ -75,19 +147,19 @@ type tokenTransport struct { username string password string authToken string - OAuthToken string + oauthToken string reuseOAuthToken bool } func newTokenTransport( - transport http.RoundTripper, username, password, authToken, OAuthToken string, reuseOAuthToken bool, + transport http.RoundTripper, username, password, authToken, oauthToken string, reuseOAuthToken bool, ) http.RoundTripper { return &tokenTransport{ transport: transport, username: username, password: password, authToken: authToken, - OAuthToken: OAuthToken, + oauthToken: oauthToken, reuseOAuthToken: reuseOAuthToken, } } @@ -131,8 +203,6 @@ func (t tokenResponse) token() string { } func (t *tokenTransport) refreshOAuthToken(failedResp *http.Response) error { - t.OAuthToken = "" - parameters := getChallengeParameters(t.scheme(), failedResp) if len(parameters) == 0 { return errors.NewForbidden(nil, "failed to refresh bearer token") @@ -180,13 +250,13 @@ func (t *tokenTransport) refreshOAuthToken(failedResp *http.Response) error { if err = decoder.Decode(&tr); err != nil { return fmt.Errorf("unable to decode token response: %s", err) } - t.OAuthToken = tr.token() + t.oauthToken = tr.token() return nil } func (t *tokenTransport) authorizeRequest(req *http.Request) error { - if t.OAuthToken != "" { - req.Header.Set("Authorization", fmt.Sprintf("%s %s", t.scheme(), t.OAuthToken)) + if t.oauthToken != "" { + req.Header.Set("Authorization", fmt.Sprintf("%s %s", t.scheme(), t.oauthToken)) } return nil } @@ -197,7 +267,7 @@ func (t *tokenTransport) RoundTrip(req *http.Request) (*http.Response, error) { if !t.reuseOAuthToken { // We usually do not re-use the OAuth token because each API call might have different scope. // But some of the provider use long life token and there is no need to refresh. - t.OAuthToken = "" + t.oauthToken = "" } }() diff --git a/internal/docker/registry/internal/transports_test.go b/internal/docker/registry/internal/transports_test.go index 213ae7098dc..bd25251ae1d 100644 --- a/internal/docker/registry/internal/transports_test.go +++ b/internal/docker/registry/internal/transports_test.go @@ -271,3 +271,275 @@ func (s *transportSuite) TestUnwrapNetError(c *gc.C) { c.Assert(unwrapedErr, jc.ErrorIs, errors.NotFound) c.Assert(unwrapedErr, gc.ErrorMatches, `Get "https://example.com": jujud-operator:2.6.6 not found`) } + +func (s *transportSuite) TestChallengeTransportTokenRefresh(c *gc.C) { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + mockRoundTripper := mocks.NewMockRoundTripper(ctrl) + + url, err := url.Parse(`https://example.com`) + c.Assert(err, jc.ErrorIsNil) + + gomock.InOrder( + // 1st try failed - bearer token was missing. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, + }, + }, + }, nil + }, + ), + // Refresh OAuth Token. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"token": "OAuth-jwt-token", "access_token": "OAuth-jwt-token","expires_in": 300}`)), + }, nil + }, + ), + // retry. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer " + `OAuth-jwt-token`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }, + ), + ) + t := internal.NewChallengeTransport(mockRoundTripper, "", "", "dXNlcm5hbWU6cHdkMQ==") + _, err = t.RoundTrip(&http.Request{ + Header: http.Header{}, + URL: url, + }) + c.Assert(err, jc.ErrorIsNil) +} + +func (s *transportSuite) TestChallengeTransportBasic(c *gc.C) { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + mockRoundTripper := mocks.NewMockRoundTripper(ctrl) + + url, err := url.Parse(`https://example.com`) + c.Assert(err, jc.ErrorIsNil) + + gomock.InOrder( + // 1st try failed - bearer token was missing. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Basic realm="my realm"`, + }, + }, + }, nil + }, + ), + // retry. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }, + ), + ) + t := internal.NewChallengeTransport(mockRoundTripper, "", "", "dXNlcm5hbWU6cHdkMQ==") + _, err = t.RoundTrip(&http.Request{ + Header: http.Header{}, + URL: url, + }) + c.Assert(err, jc.ErrorIsNil) +} + +func (s *transportSuite) TestChallengeTransportMulti(c *gc.C) { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + mockRoundTripper := mocks.NewMockRoundTripper(ctrl) + + url, err := url.Parse(`https://example.com`) + c.Assert(err, jc.ErrorIsNil) + + gomock.InOrder( + // 1st try failed - bearer token was missing. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Basic realm="my realm"`, + `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, + }, + }, + }, nil + }, + ), + // retry. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusUnauthorized, Body: io.NopCloser(nil)}, nil + }, + ), + // Refresh OAuth Token. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"token": "OAuth-jwt-token", "access_token": "OAuth-jwt-token","expires_in": 300}`)), + }, nil + }, + ), + // retry. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer " + `OAuth-jwt-token`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }, + ), + + // re-use last successful + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Basic realm="my realm"`, + `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, + }, + }, + }, nil + }, + ), + // Refresh OAuth Token. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"token": "OAuth-jwt-token", "access_token": "OAuth-jwt-token","expires_in": 300}`)), + }, nil + }, + ), + // retry. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer " + `OAuth-jwt-token`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }, + ), + + // re-use last successful + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Basic realm="my realm"`, + `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, + }, + }, + }, nil + }, + ), + // Refresh OAuth Token. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"token": "OAuth-jwt-token", "access_token": "OAuth-jwt-token","expires_in": 300}`)), + }, nil + }, + ), + // still bad + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer " + `OAuth-jwt-token`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{ + Request: req, + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(nil), + Header: http.Header{ + http.CanonicalHeaderKey("WWW-Authenticate"): []string{ + `Basic realm="my realm"`, + `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, + }, + }, + }, nil + }, + ), + // retry with basic again. + mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( + func(req *http.Request) (*http.Response, error) { + c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + `dXNlcm5hbWU6cHdkMQ==`}}) + c.Assert(req.URL.String(), gc.Equals, `https://example.com`) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }, + ), + ) + t := internal.NewChallengeTransport(mockRoundTripper, "", "", "dXNlcm5hbWU6cHdkMQ==") + _, err = t.RoundTrip(&http.Request{ + Header: http.Header{}, + URL: url, + }) + c.Assert(err, jc.ErrorIsNil) + + // Reuse + _, err = t.RoundTrip(&http.Request{ + Header: http.Header{}, + URL: url, + }) + c.Assert(err, jc.ErrorIsNil) + + // Reauth + _, err = t.RoundTrip(&http.Request{ + Header: http.Header{}, + URL: url, + }) + c.Assert(err, jc.ErrorIsNil) +} diff --git a/provider/azure/internal/azureauth/discovery.go b/provider/azure/internal/azureauth/discovery.go index 68cb776be9f..a9587913e08 100644 --- a/provider/azure/internal/azureauth/discovery.go +++ b/provider/azure/internal/azureauth/discovery.go @@ -29,9 +29,7 @@ type recordAuthHeaderPolicy struct { func (p *recordAuthHeaderPolicy) Do(req *policy.Request) (*http.Response, error) { resp, err := req.Next() - if err == nil { - p.authHeader = resp.Header.Get(authenticateHeaderKey) - } + p.authHeader = resp.Header.Get(authenticateHeaderKey) return resp, err } diff --git a/provider/azure/internal/errorutils/errors_test.go b/provider/azure/internal/errorutils/errors_test.go index e4080e09b61..e945503a4a9 100644 --- a/provider/azure/internal/errorutils/errors_test.go +++ b/provider/azure/internal/errorutils/errors_test.go @@ -79,6 +79,8 @@ func (s *ErrorSuite) TestAuthRelatedStatusCodes(c *gc.C) { for t := range common.AuthorisationFailureStatusCodes { called = false s.azureError.StatusCode = t + s.azureError.ErrorCode = "some error code" + s.azureError.RawResponse = &http.Response{} errorutils.HandleCredentialError(s.azureError, ctx) c.Assert(called, jc.IsTrue) } diff --git a/rpc/params/internal.go b/rpc/params/internal.go index 356d4df2c8f..3b48059cb5e 100644 --- a/rpc/params/internal.go +++ b/rpc/params/internal.go @@ -314,6 +314,7 @@ type CommitHookChangesArg struct { SetUnitState *SetUnitStateArg `json:"unit-state,omitempty"` AddStorage []StorageAddParams `json:"add-storage,omitempty"` SecretCreates []CreateSecretArg `json:"secret-creates,omitempty"` + TrackLatest []string `json:"secret-track-latest,omitempty"` SecretUpdates []UpdateSecretArg `json:"secret-updates,omitempty"` SecretGrants []GrantRevokeSecretArg `json:"secret-grants,omitempty"` SecretRevokes []GrantRevokeSecretArg `json:"secret-revokes,omitempty"` diff --git a/rpc/params/migration.go b/rpc/params/migration.go index eafded950b7..1926802b328 100644 --- a/rpc/params/migration.go +++ b/rpc/params/migration.go @@ -163,11 +163,12 @@ type MasterMigrationStatus struct { // MigrationModelInfo is used to report basic model information to the // migrationmaster worker. type MigrationModelInfo struct { - UUID string `json:"uuid"` - Name string `json:"name"` - OwnerTag string `json:"owner-tag"` - AgentVersion version.Number `json:"agent-version"` - ControllerAgentVersion version.Number `json:"controller-agent-version"` + UUID string `json:"uuid"` + Name string `json:"name"` + OwnerTag string `json:"owner-tag"` + AgentVersion version.Number `json:"agent-version"` + ControllerAgentVersion version.Number `json:"controller-agent-version"` + FacadeVersions map[string][]int `json:"facade-versions,omitempty"` } // MigrationStatus reports the current status of a model migration. diff --git a/state/application.go b/state/application.go index 01a9c851da6..a6c4fa3161a 100644 --- a/state/application.go +++ b/state/application.go @@ -775,13 +775,13 @@ func (a *Application) removeOps(asserts bson.D, op *ForcedOperation) ([]txn.Op, return nil, errors.Trace(err) } ops = append(ops, secretConsumerPermissionsOps...) - secretLabelOps, err := a.st.removeOwnerSecretLabelOps(a.ApplicationTag()) + secretLabelOps, err := a.st.removeOwnerSecretLabelsOps(a.ApplicationTag()) if err != nil { return nil, errors.Trace(err) } ops = append(ops, secretLabelOps...) - secretLabelOps, err = a.st.removeConsumerSecretLabelOps(a.ApplicationTag()) + secretLabelOps, err = a.st.removeConsumerSecretLabelsOps(a.ApplicationTag()) if err != nil { return nil, errors.Trace(err) } @@ -2963,11 +2963,11 @@ func (a *Application) removeUnitOps(store objectstore.ObjectStore, u *Unit, asse if op.FatalError(err) { return nil, errors.Trace(err) } - secretOwnerLabelOps, err := a.st.removeOwnerSecretLabelOps(u.Tag()) + secretOwnerLabelOps, err := a.st.removeOwnerSecretLabelsOps(u.Tag()) if op.FatalError(err) { return nil, errors.Trace(err) } - secretConsumerLabelOps, err := a.st.removeConsumerSecretLabelOps(u.Tag()) + secretConsumerLabelOps, err := a.st.removeConsumerSecretLabelsOps(u.Tag()) if op.FatalError(err) { return nil, errors.Trace(err) } diff --git a/state/migration_import_tasks.go b/state/migration_import_tasks.go index 3a64b93e3c2..242bf4539c0 100644 --- a/state/migration_import_tasks.go +++ b/state/migration_import_tasks.go @@ -650,6 +650,14 @@ func (ImportSecrets) Execute(src SecretsInput, runner TransactionRunner, knownSe return errors.Annotatef(err, "invalid consumer for secret %q", secret.Id()) } key := src.SecretConsumerKey(uri, consumer.String()) + currentRev := info.CurrentRevision() + latestRev := info.LatestRevision() + // Older models may have set the consumed rev info to 0 (assuming the latest revision always). + // So set the latest values explicitly. + if currentRev == 0 { + currentRev = secret.LatestRevision() + latestRev = secret.LatestRevision() + } ops = append(ops, txn.Op{ C: secretConsumersC, Id: key, @@ -658,8 +666,8 @@ func (ImportSecrets) Execute(src SecretsInput, runner TransactionRunner, knownSe DocID: key, ConsumerTag: consumer.String(), Label: info.Label(), - CurrentRevision: info.CurrentRevision(), - LatestRevision: info.LatestRevision(), + CurrentRevision: currentRev, + LatestRevision: latestRev, }, }) } diff --git a/state/migration_import_test.go b/state/migration_import_test.go index ff7d9d0f54b..c56e285b9cb 100644 --- a/state/migration_import_test.go +++ b/state/migration_import_test.go @@ -3246,6 +3246,51 @@ func (s *MigrationImportSuite) TestSecrets(c *gc.C) { c.Assert(backendRefCount, gc.Equals, 2) } +func (s *MigrationImportSuite) TestSecretsEnsureConsumerRevisionInfo(c *gc.C) { + store := state.NewSecrets(s.State) + owner := s.Factory.MakeApplication(c, nil) + uri := secrets.NewURI() + p := state.CreateSecretParams{ + Version: 1, + Owner: owner.Tag(), + UpdateSecretParams: state.UpdateSecretParams{ + LeaderToken: &fakeToken{}, + RotatePolicy: ptr(secrets.RotateNever), + Data: map[string]string{"foo": "bar"}, + }, + } + md, err := store.CreateSecret(uri, p) + c.Assert(err, jc.ErrorIsNil) + + consumer := s.Factory.MakeApplication(c, &factory.ApplicationParams{ + Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ + Name: "wordpress", + }), + }) + err = s.State.SaveSecretConsumer(uri, consumer.Tag(), &secrets.SecretConsumerMetadata{ + Label: "consumer label", + CurrentRevision: 0, + LatestRevision: 0, + }) + c.Assert(err, jc.ErrorIsNil) + + _, newSt := s.importModel(c, s.State) + + store = state.NewSecrets(newSt) + all, err := store.ListSecrets(state.SecretsFilter{}) + c.Assert(err, jc.ErrorIsNil) + c.Assert(all, gc.HasLen, 1) + c.Assert(all[0], jc.DeepEquals, md) + + info, err := newSt.GetSecretConsumer(uri, consumer.Tag()) + c.Assert(err, jc.ErrorIsNil) + c.Assert(info, jc.DeepEquals, &secrets.SecretConsumerMetadata{ + Label: "consumer label", + CurrentRevision: 1, + LatestRevision: 1, + }) +} + func (s *MigrationImportSuite) TestSecretsMissingBackend(c *gc.C) { store := state.NewSecrets(s.State) owner := s.Factory.MakeApplication(c, nil) diff --git a/state/secrets.go b/state/secrets.go index 3c615b1006f..08f8caddf11 100644 --- a/state/secrets.go +++ b/state/secrets.go @@ -397,6 +397,13 @@ func (s *secretsStore) UpdateSecret(uri *secrets.URI, p UpdateSecretParams) (*se if p.Label != nil && *p.Label != metadataDoc.Label { // OwnerTag has already been validated. owner, _ := names.ParseTag(metadataDoc.OwnerTag) + if metadataDoc.Label != "" { + removeOldLabelOps, err := s.st.removeOwnerSecretLabelOps(owner, metadataDoc.Label) + if err != nil { + return nil, errors.Trace(err) + } + ops = append(ops, removeOldLabelOps...) + } uniqueLabelOps, err := s.st.uniqueSecretOwnerLabelOps(owner, *p.Label) if err != nil { return nil, errors.Trace(err) @@ -1279,18 +1286,28 @@ func (st *State) uniqueSecretConsumerLabelOps(consumerTag names.Tag, label strin return append(ops, ops2...), nil } +// uniqueSecretLabelBaseOps is used when creating or updating a secret with an owner label, or +// when saving a secret consumer record. It checks that the label is not used twice: +// - a unit of the same application consuming an application owned secret cannot use the same label +// as is used in the secret metadata of any application owned secret. +// The check is done when creating a new application owned secret, or saving a consumer record. func (st *State) uniqueSecretLabelBaseOps(tag names.Tag, label string) (ops []txn.Op, _ error) { col, close := st.db().GetCollection(refcountsC) defer close() - var keyPattern string + var ( + keyPattern string + errorMsg string + ) + switch tag := tag.(type) { case names.ApplicationTag: - // Ensure no units use this label for both owner and consumer label.. + // Ensure no units use this label for both owner and consumer label. keyPattern = fmt.Sprintf( "^%s:(%s|%s)#unit-%s-[0-9]+#%s$", st.ModelUUID(), secretOwnerLabelKeyPrefix, secretConsumerLabelKeyPrefix, tag.Name, label, ) + errorMsg = fmt.Sprintf("secret label %q for a unit of application %q already exists", label, tag.Name) case names.UnitTag: // Ensure no application owned secret uses this label. applicationName, _ := names.UnitApplication(tag.Id()) @@ -1300,8 +1317,10 @@ func (st *State) uniqueSecretLabelBaseOps(tag names.Tag, label string) (ops []tx "^%s:(%s|%s)#%s#%s$", st.ModelUUID(), secretOwnerLabelKeyPrefix, secretConsumerLabelKeyPrefix, appTag.String(), label, ) + errorMsg = fmt.Sprintf("secret label %q for application %q already exists", label, applicationName) case names.ModelTag: keyPattern = fmt.Sprintf("^%s:%s#%s#%s$", st.ModelUUID(), secretOwnerLabelKeyPrefix, tag.String(), label) + errorMsg = fmt.Sprintf("user secret label %q already exists", label) default: return nil, errors.NotSupportedf("tag type %T", tag) } @@ -1311,7 +1330,7 @@ func (st *State) uniqueSecretLabelBaseOps(tag names.Tag, label string) (ops []tx return nil, errors.Trace(err) } if count > 0 { - return nil, errors.WithType(errors.Errorf("secret label %q for %q already exists", label, tag), LabelExists) + return nil, errors.WithType(errors.New(errorMsg), LabelExists) } return []txn.Op{ @@ -1346,11 +1365,34 @@ func (st *State) uniqueSecretLabelOpsRaw(tag names.Tag, label, role string, keyG return []txn.Op{countOp, incOp}, nil } -func (st *State) removeOwnerSecretLabelOps(ownerTag names.Tag) ([]txn.Op, error) { +func (st *State) removeOwnerSecretLabelOps(ownerTag names.Tag, label string) ([]txn.Op, error) { + refCountCollection, ccloser := st.db().GetCollection(refcountsC) + defer ccloser() + + key := secretOwnerLabelKey(ownerTag, label) + countOp, count, err := nsRefcounts.CurrentOp(refCountCollection, key) + if err != nil { + return nil, errors.Trace(err) + } + if count == 0 { + return []txn.Op{countOp}, nil + } + + return []txn.Op{ + { + C: refcountsC, + Id: secretOwnerLabelKey(ownerTag, label), + Assert: txn.DocExists, + Remove: true, + }, + }, nil +} + +func (st *State) removeOwnerSecretLabelsOps(ownerTag names.Tag) ([]txn.Op, error) { return st.removeSecretLabelOps(ownerTag, secretOwnerLabelKey) } -func (st *State) removeConsumerSecretLabelOps(consumerTag names.Tag) ([]txn.Op, error) { +func (st *State) removeConsumerSecretLabelsOps(consumerTag names.Tag) ([]txn.Op, error) { return st.removeSecretLabelOps(consumerTag, secretConsumerLabelKey) } diff --git a/state/secrets_test.go b/state/secrets_test.go index 6a5fde18edf..3cae3b9defd 100644 --- a/state/secrets_test.go +++ b/state/secrets_test.go @@ -134,7 +134,7 @@ func (s *SecretsSuite) TestCreateUserSecret(c *gc.C) { uri2 := secrets.NewURI() _, err = s.store.CreateSecret(uri2, p) - c.Assert(err, gc.ErrorMatches, fmt.Sprintf(`secret label "label-1" for %q already exists`, s.Model.Tag().String())) + c.Assert(err, gc.ErrorMatches, fmt.Sprintf(`user secret label "label-1" already exists`)) } func (s *SecretsSuite) TestCreateBackendRef(c *gc.C) { @@ -884,6 +884,33 @@ func (s *SecretsSuite) TestUpdateDataSetsLatestConsumerRevision(c *gc.C) { }) } +func (s *SecretsSuite) TestUpdateOwnerLabel(c *gc.C) { + uri := secrets.NewURI() + now := s.Clock.Now().Round(time.Second).UTC() + next := now.Add(time.Minute).Round(time.Second).UTC() + cp := state.CreateSecretParams{ + Version: 1, + Owner: s.owner.Tag(), + UpdateSecretParams: state.UpdateSecretParams{ + LeaderToken: &fakeToken{}, + RotatePolicy: ptr(secrets.RotateDaily), + NextRotateTime: ptr(next), + Data: map[string]string{"foo": "bar"}, + }, + } + md, err := s.store.CreateSecret(uri, cp) + c.Assert(err, jc.ErrorIsNil) + s.assertUpdatedSecret(c, md, 1, state.UpdateSecretParams{ + LeaderToken: &fakeToken{}, + Label: ptr("foobar2"), + }) + // Ensure it can be reset back to an older value. + s.assertUpdatedSecret(c, md, 1, state.UpdateSecretParams{ + LeaderToken: &fakeToken{}, + Label: ptr("foobar"), + }) +} + func (s *SecretsSuite) TestUpdateDataSetsLatestConsumerRevisionConcurrentAdd(c *gc.C) { uri := secrets.NewURI() now := s.Clock.Now().Round(time.Second).UTC() diff --git a/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/charmcraft.yaml b/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/charmcraft.yaml new file mode 100644 index 00000000000..684e6403ada --- /dev/null +++ b/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/charmcraft.yaml @@ -0,0 +1,21 @@ +type: charm +parts: + charm: + prime: + - dispatch + - hooks + - README.md + - LICENSE + - version + - src + - actions.yaml + - metadata.yaml + - config.yaml +bases: + - build-on: + - name: "ubuntu" + channel: "22.04" + run-on: + - name: "ubuntu" + channel: "22.04" + architectures: ["amd64", "arm64"] diff --git a/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/version b/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/version new file mode 100644 index 00000000000..956a31b8059 --- /dev/null +++ b/testcharms/charm-hub/charms/juju-qa-test/3-0-stable/version @@ -0,0 +1 @@ +3.0-stable diff --git a/testcharms/charm-hub/charms/juju-qa-test/Makefile b/testcharms/charm-hub/charms/juju-qa-test/Makefile index 393eec0535a..be34c9ed000 100644 --- a/testcharms/charm-hub/charms/juju-qa-test/Makefile +++ b/testcharms/charm-hub/charms/juju-qa-test/Makefile @@ -30,3 +30,9 @@ stable: @echo Creating the '2.0/edge' version of juju-qa-test cp -r 2-0-edge/* . charmcraft pack + +30stable: + @echo Creating the '3.0/stable' version of juju-qa-test + cp -r 3-0-stable/* . + charmcraft pack + diff --git a/testcharms/charm-hub/charms/juju-qa-test/README.md b/testcharms/charm-hub/charms/juju-qa-test/README.md index ec771240baa..afc5f601550 100644 --- a/testcharms/charm-hub/charms/juju-qa-test/README.md +++ b/testcharms/charm-hub/charms/juju-qa-test/README.md @@ -24,6 +24,7 @@ Get your fortune | 2.0-edge | 21 | latest/edge | groovy, jammy, focal, bionic, xenial | | 2.0-stable | 22 | 2.0/stable | disco, bionic, xenial, trusty | | 2.0-edge | 23 | 2.0/edge | disco, bionic, xenial, trusty | +| 3.0-stable | 27 | 2.3/stable | jammy | To publish new versions of stable/candidate/edge see the files in the subdirectories 'stable', 'candidate', 'edge' respectively. You should be able diff --git a/testcharms/charms/lxd-profile-subordinate/charmcraft.yaml b/testcharms/charms/lxd-profile-subordinate/charmcraft.yaml index 490e3ea46e3..8250bcd9aa9 100644 --- a/testcharms/charms/lxd-profile-subordinate/charmcraft.yaml +++ b/testcharms/charms/lxd-profile-subordinate/charmcraft.yaml @@ -6,28 +6,28 @@ bases: architectures: ["amd64", "arm64"] run-on: - name: "ubuntu" - channel: "16.04" + channel: "22.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "18.04" + channel: "20.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "20.04" + channel: "18.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "22.04" + channel: "16.04" architectures: - amd64 - aarch64 diff --git a/testcharms/charms/lxd-profile-subordinate/metadata.yaml b/testcharms/charms/lxd-profile-subordinate/metadata.yaml index 0b44d6ef3f4..1e9bc537a1a 100644 --- a/testcharms/charms/lxd-profile-subordinate/metadata.yaml +++ b/testcharms/charms/lxd-profile-subordinate/metadata.yaml @@ -15,8 +15,8 @@ requires: interface: juju-info scope: container series: - - quantal - - xenial - - bionic - - focal - jammy + - focal + - bionic + - xenial + - quantal diff --git a/testcharms/charms/lxd-profile-without-devices/charmcraft.yaml b/testcharms/charms/lxd-profile-without-devices/charmcraft.yaml index e9c0f138282..bcce8076215 100644 --- a/testcharms/charms/lxd-profile-without-devices/charmcraft.yaml +++ b/testcharms/charms/lxd-profile-without-devices/charmcraft.yaml @@ -6,28 +6,28 @@ bases: architectures: ["amd64", "arm64"] run-on: - name: "ubuntu" - channel: "16.04" + channel: "22.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "18.04" + channel: "20.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "20.04" + channel: "16.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "22.04" + channel: "18.04" architectures: - amd64 - aarch64 diff --git a/testcharms/charms/lxd-profile-without-devices/metadata.yaml b/testcharms/charms/lxd-profile-without-devices/metadata.yaml index 2a30fb52ad7..b7b9fad25b0 100644 --- a/testcharms/charms/lxd-profile-without-devices/metadata.yaml +++ b/testcharms/charms/lxd-profile-without-devices/metadata.yaml @@ -13,8 +13,9 @@ tags: - application_development subordinate: false series: - - xenial - - bionic - - focal - jammy + - focal + - bionic + - xenial + diff --git a/testcharms/charms/lxd-profile/charmcraft.yaml b/testcharms/charms/lxd-profile/charmcraft.yaml index b40b7c97845..db01eb51f47 100644 --- a/testcharms/charms/lxd-profile/charmcraft.yaml +++ b/testcharms/charms/lxd-profile/charmcraft.yaml @@ -6,28 +6,28 @@ bases: architectures: ["amd64", "arm64"] run-on: - name: "ubuntu" - channel: "16.04" + channel: "22.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "18.04" + channel: "20.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "20.04" + channel: 18.04" architectures: - amd64 - aarch64 - arm64 - s390x - name: "ubuntu" - channel: "22.04" + channel: "16.04" architectures: - amd64 - aarch64 diff --git a/testcharms/charms/lxd-profile/metadata.yaml b/testcharms/charms/lxd-profile/metadata.yaml index 152eb98a2df..4aedca92d10 100644 --- a/testcharms/charms/lxd-profile/metadata.yaml +++ b/testcharms/charms/lxd-profile/metadata.yaml @@ -13,8 +13,8 @@ tags: - application_development subordinate: false series: - - xenial - - bionic - - focal - jammy + - focal + - bionic + - xenial diff --git a/tests/includes/juju.sh b/tests/includes/juju.sh index 167aac043fe..1b65116f5e6 100644 --- a/tests/includes/juju.sh +++ b/tests/includes/juju.sh @@ -455,7 +455,7 @@ destroy_controller() { echo "====> Destroying juju ($(green "${name}"))" if [[ ${KILL_CONTROLLER:-} != "true" ]]; then - echo "${name}" | xargs -I % juju destroy-controller --destroy-all-models --no-prompt % 2>&1 | OUTPUT "${output}" + echo "${name}" | xargs -I % juju destroy-controller --destroy-all-models --destroy-storage --no-prompt % 2>&1 | OUTPUT "${output}" else echo "${name}" | xargs -I % juju kill-controller -t 0 --no-prompt % 2>&1 | OUTPUT "${output}" fi diff --git a/tests/suites/coslite/cl.sh b/tests/suites/coslite/cl.sh index 5cc38fec4da..20679fbbe27 100644 --- a/tests/suites/coslite/cl.sh +++ b/tests/suites/coslite/cl.sh @@ -8,6 +8,7 @@ run_deploy_coslite() { file="${TEST_DIR}/${model_name}.log" ensure "${model_name}" "${file}" + juju deploy ubuntu-lite # deploy ubuntu-lite for a container that can easily access cos-lite components on all k8s juju deploy cos-lite --trust --channel=stable juju config traefik external_hostname=test-coslite.com echo "Wait for all unit agents to be in idle condition" @@ -22,15 +23,15 @@ run_deploy_coslite() { echo "check if alertmanager is ready" alertmanager_ip=$(juju status --format=json | jq -r '.applications.alertmanager.units."alertmanager/0".address') - check_ready "http://$alertmanager_ip:9093" 200 + check_ready "http://$alertmanager_ip:9093/-/ready" 200 echo "check if grafana is ready" grafana_ip=$(juju status --format=json | jq -r '.applications.grafana.units."grafana/0".address') - check_ready "http://$grafana_ip:3000" 200 + check_ready "http://$grafana_ip:3000/api/health" 200 echo "check if prometheus is ready" prometheus_ip=$(juju status --format=json | jq -r '.applications.prometheus.units."prometheus/0".address') - check_ready "http://$prometheus_ip:9090" 200 + check_ready "http://$prometheus_ip:9090/-/ready" 200 echo "cos lite tests passed" } @@ -42,12 +43,12 @@ check_ready() { code=${2} attempt=1 while true; do - status_code=$(curl --write-out "%{http_code}" -L --silent --output /dev/null "${url}") + status_code=$(juju ssh ubuntu-lite/0 curl --write-out "%{http_code}" -L --silent --output /dev/null "${url}") if [[ $status_code -eq $code ]]; then echo "Ready to serve traffic" break fi - if [[ ${attempt} -ge 3 ]]; then + if [[ ${attempt} -ge 5 ]]; then echo "Failed to connect to ${url} after ${attempt} attempts with status code ${status_code}" exit 1 fi diff --git a/tests/suites/coslite/task.sh b/tests/suites/coslite/task.sh index 825fc74f25d..193d92af24a 100644 --- a/tests/suites/coslite/task.sh +++ b/tests/suites/coslite/task.sh @@ -15,20 +15,11 @@ test_coslite() { case "${BOOTSTRAP_PROVIDER:-}" in "k8s") - # disable metallb then enable it with a new set of out ipaddr - microk8s disable metallb - IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') - microk8s enable metallb:"$IPADDR"-"$IPADDR" - microk8s kubectl rollout status deployments/hostpath-provisioner -n kube-system -w - microk8s kubectl rollout status deployments/coredns -n kube-system -w - microk8s kubectl rollout status daemonset.apps/speaker -n metallb-system -w - test_deploy_coslite ;; *) echo "==> TEST SKIPPED: test_deploy_coslite test runs on k8s only" ;; - esac # TODO(basebandit): remove KILL_CONTROLLER once model teardown has been fixed for k8s models. diff --git a/tests/suites/deploy/charms/lxd-profile-alt/manifest.yaml b/tests/suites/deploy/charms/lxd-profile-alt/manifest.yaml new file mode 100644 index 00000000000..218f3f846c6 --- /dev/null +++ b/tests/suites/deploy/charms/lxd-profile-alt/manifest.yaml @@ -0,0 +1,26 @@ +bases: + - name: ubuntu + channel: 22.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 20.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 18.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 16.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 12.04/stable + architectures: + - amd64 + - arm64 diff --git a/tests/suites/deploy/charms/lxd-profile/manifest.yaml b/tests/suites/deploy/charms/lxd-profile/manifest.yaml new file mode 100644 index 00000000000..218f3f846c6 --- /dev/null +++ b/tests/suites/deploy/charms/lxd-profile/manifest.yaml @@ -0,0 +1,26 @@ +bases: + - name: ubuntu + channel: 22.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 20.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 18.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 16.04/stable + architectures: + - amd64 + - arm64 + - name: ubuntu + channel: 12.04/stable + architectures: + - amd64 + - arm64 diff --git a/tests/suites/deploy/deploy_bundles.sh b/tests/suites/deploy/deploy_bundles.sh index 20b03c21183..84a4b9dbeb0 100644 --- a/tests/suites/deploy/deploy_bundles.sh +++ b/tests/suites/deploy/deploy_bundles.sh @@ -378,11 +378,20 @@ test_deploy_bundles() { run "run_deploy_charmhub_bundle" run "run_deploy_multi_app_single_charm_bundle" + # LXD specific profile tests. case "${BOOTSTRAP_PROVIDER:-}" in "lxd" | "localhost") run "run_deploy_lxd_profile_bundle_openstack" + echo "==> TEST SKIPPED: deploy_lxd_profile_bundle - tests for non LXD only" + ;; + *) + echo "==> TEST SKIPPED: deploy_lxd_profile_bundle_openstack - tests for LXD only" run "run_deploy_lxd_profile_bundle" ;; + esac + + # AWS specific image id tests. + case "${BOOTSTRAP_PROVIDER:-}" in "ec2" | "aws") check_dependencies aws add_clean_func "run_cleanup_ami" @@ -394,8 +403,6 @@ test_deploy_bundles() { run "run_deploy_bundle_overlay_with_image_id_no_base" ;; *) - echo "==> TEST SKIPPED: deploy_lxd_profile_bundle_openstack - tests for LXD only" - echo "==> TEST SKIPPED: deploy_lxd_profile_bundle - tests for LXD only" echo "==> TEST SKIPPED: deploy_bundle_with_image_id - tests for AWS only" echo "==> TEST SKIPPED: deploy_bundle_with_image_id_on_base_bundle - tests for AWS only" echo "==> TEST SKIPPED: deploy_bundle_with_image_id_no_base - tests for AWS only" diff --git a/tests/suites/deploy/deploy_charms.sh b/tests/suites/deploy/deploy_charms.sh index 250d7ff9db2..0521c688078 100644 --- a/tests/suites/deploy/deploy_charms.sh +++ b/tests/suites/deploy/deploy_charms.sh @@ -14,6 +14,23 @@ run_deploy_charm() { destroy_model "test-deploy-charm" } +run_deploy_charm_unsupported_series() { + # Test trying to deploy a charmhub charm to an operating system + # never supported in the specified channel. It should fail. + echo + + testname="test-deploy-charm-unsupported-series" + file="${TEST_DIR}/${testname}.log" + + ensure "${testname}" "${file}" + + # The charm in 3.0/stable only supports jammy and only + # one charm has been released to that channel. + juju deploy juju-qa-test --channel 3.0/stable --series focal | grep -q 'charm or bundle not found for channel' || true + + destroy_model "${testname}" +} + run_deploy_specific_series() { echo @@ -47,7 +64,10 @@ run_deploy_lxd_profile_charm() { ensure "test-deploy-lxd-profile" "${file}" - juju deploy juju-qa-lxd-profile-without-devices + # This charm deploys to Xenial by default, which doesn't + # always result in a machine which becomes fully deployed + # with the lxd provider. + juju deploy juju-qa-lxd-profile-without-devices --series jammy wait_for "lxd-profile-without-devices" "$(idle_condition "lxd-profile-without-devices")" juju status --format=json | jq '.machines | .["0"] | .["lxd-profiles"] | keys[0]' | check "juju-test-deploy-lxd-profile-lxd-profile" @@ -62,7 +82,10 @@ run_deploy_lxd_profile_charm_container() { ensure "test-deploy-lxd-profile-container" "${file}" - juju deploy juju-qa-lxd-profile-without-devices --to lxd + # This charm deploys to Xenial by default, which doesn't + # always result in a machine which becomes fully deployed + # with the lxd provider. + juju deploy juju-qa-lxd-profile-without-devices --to lxd --series jammy wait_for "lxd-profile-without-devices" "$(idle_condition "lxd-profile-without-devices")" juju status --format=json | jq '.machines | .["0"] | .containers | .["0/lxd/0"] | .["lxd-profiles"] | keys[0]' | @@ -121,14 +144,6 @@ run_deploy_local_lxd_profile_charm() { juju status --format=json | jq "${machine_1}" | check "${lxd_profile_name}" juju status --format=json | jq "${machine_1}" | check "${lxd_profile_sub_name}" - juju add-unit "lxd-profile" --to lxd - - machine_2="$(machine_container_path 2 2/lxd/0)" - wait_for "${lxd_profile_sub_name}" "${machine_2}" - - juju status --format=json | jq "${machine_2}" | check "${lxd_profile_name}" - juju status --format=json | jq "${machine_2}" | check "${lxd_profile_sub_name}" - destroy_model "test-deploy-local-lxd-profile" } @@ -198,6 +213,8 @@ run_deploy_lxd_to_machine() { } run_deploy_lxd_to_container() { + # Ensure profiles get applied correctly to containers + # and 1 gets added if a subordinate is added. echo model_name="test-deploy-lxd-container" @@ -208,7 +225,20 @@ run_deploy_lxd_to_container() { charm=./tests/suites/deploy/charms/lxd-profile-alt juju deploy "${charm}" --to lxd + juju deploy ./testcharms/charms/lxd-profile-subordinate + juju add-relation lxd-profile-subordinate lxd-profile-alt + wait_for "lxd-profile-alt" "$(idle_condition "lxd-profile-alt")" + wait_for "lxd-profile-subordinate" ".applications | keys[1]" + + machine_0="$(machine_container_path 0 0/lxd/0)" + wait_for "lxd-profile-subordinate" "${machine_0}" + + lxd_profile_name="juju-test-deploy-lxd-container-lxd-profile-alt" + lxd_profile_sub_name="juju-test-deploy-lxd-container-lxd-profile-subordinate" + + juju status --format=json | jq "${machine_0}" | check "${lxd_profile_name}" + juju status --format=json | jq "${machine_0}" | check "${lxd_profile_sub_name}" OUT=$(juju exec --machine 0 -- sh -c 'sudo lxc profile show "juju-test-deploy-lxd-container-lxd-profile-alt-0"') echo "${OUT}" | grep -E "linux.kernel_modules: ([a-zA-Z0-9\_,]+)?ip_tables,ip6_tables([a-zA-Z0-9\_,]+)?" @@ -289,9 +319,8 @@ test_deploy_charms() { run "run_deploy_charm" run "run_deploy_specific_series" - run "run_deploy_lxd_to_container" - run "run_deploy_lxd_profile_charm_container" run "run_resolve_charm" + run "run_deploy_charm_unsupported_series" case "${BOOTSTRAP_PROVIDER:-}" in "lxd" | "localhost") @@ -299,11 +328,15 @@ test_deploy_charms() { run "run_deploy_lxd_profile_charm" run "run_deploy_local_predeployed_charm" run "run_deploy_local_lxd_profile_charm" + echo "==> TEST SKIPPED: deploy_lxd_to_container - tests for non LXD only" + echo "==> TEST SKIPPED: deploy_lxd_profile_charm_container - tests for non LXD only" ;; *) echo "==> TEST SKIPPED: deploy_lxd_to_machine - tests for LXD only" echo "==> TEST SKIPPED: deploy_lxd_profile_charm - tests for LXD only" echo "==> TEST SKIPPED: deploy_local_lxd_profile_charm - tests for LXD only" + run "run_deploy_lxd_to_container" + run "run_deploy_lxd_profile_charm_container" ;; esac ) diff --git a/tests/suites/secrets_iaas/vault.sh b/tests/suites/secrets_iaas/vault.sh index 133617e2c74..0652b548ac0 100644 --- a/tests/suites/secrets_iaas/vault.sh +++ b/tests/suites/secrets_iaas/vault.sh @@ -143,15 +143,11 @@ prepare_vault() { sudo snap install vault fi + # If no databases are related, vault will be auto configured to + # use its embedded raft storage backend for storage and HA. juju --show-log deploy vault - juju --show-log deploy -n 3 mysql-innodb-cluster - juju --show-log deploy mysql-router - juju --show-log integrate mysql-router:db-router mysql-innodb-cluster:db-router - juju --show-log integrate mysql-router:shared-db vault:shared-db juju --show-log expose vault - wait_for "active" '.applications["mysql-innodb-cluster"] | ."application-status".current' 1200 - wait_for "active" '.applications["mysql-router"] | ."application-status".current' 1200 wait_for "blocked" "$(workload_status vault 0).current" vault_public_addr=$(juju status --format json | jq -r '.applications.vault.units."vault/0"."public-address"') export VAULT_ADDR="http://${vault_public_addr}:8200" @@ -166,6 +162,17 @@ prepare_vault() { vault operator unseal "$unseal_key0" vault operator unseal "$unseal_key1" vault operator unseal "$unseal_key2" + + # wait for vault server to be ready. + attempt=0 + until [[ $(vault status -format yaml 2>/dev/null | yq .initialized | grep -i 'true') ]]; do + if [[ ${attempt} -ge 30 ]]; then + echo "Failed: vault server was not able to be ready." + exit 1 + fi + sleep 2 + attempt=$((attempt + 1)) + done } test_secret_drain() { diff --git a/worker/caasrbacmapper/mapper.go b/worker/caasrbacmapper/mapper.go index 21bea2f6af0..9a8204bc900 100644 --- a/worker/caasrbacmapper/mapper.go +++ b/worker/caasrbacmapper/mapper.go @@ -110,13 +110,16 @@ func NewMapper(logger Logger, informer core.ServiceAccountInformer) (*DefaultMap workQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), } - dm.saInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := dm.saInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: dm.enqueueServiceAccount, DeleteFunc: dm.enqueueServiceAccount, UpdateFunc: func(_, newObj interface{}) { dm.enqueueServiceAccount(newObj) }, }) + if err != nil { + return nil, errors.Trace(err) + } if err := catacomb.Invoke(catacomb.Plan{ Site: &dm.catacomb, diff --git a/worker/caasrbacmapper/mapper_test.go b/worker/caasrbacmapper/mapper_test.go index 7b46e28905e..fc21f0f8114 100644 --- a/worker/caasrbacmapper/mapper_test.go +++ b/worker/caasrbacmapper/mapper_test.go @@ -57,7 +57,7 @@ func (m *MapperSuite) TestMapperAdditionSync(c *gc.C) { DoAndReturn(func(h cache.ResourceEventHandlerFuncs) { eventHandlers = h waitGroup.Done() - }) + }).Return(m.mockSharedIndexInformer, nil) mapper, err := caasrbacmapper.NewMapper(loggo.Logger{}, m.mockSAInformer) c.Assert(err, jc.ErrorIsNil) @@ -85,7 +85,7 @@ func (m *MapperSuite) TestMapperAdditionSync(c *gc.C) { return sa, nil }) - eventHandlers.OnAdd(sa) + eventHandlers.OnAdd(sa, false) waitGroup.Wait() mapper.Kill() @@ -112,7 +112,7 @@ func (m *MapperSuite) TestRBACMapperUpdateSync(c *gc.C) { DoAndReturn(func(h cache.ResourceEventHandlerFuncs) { eventHandlers = h waitGroup.Done() - }) + }).Return(m.mockSharedIndexInformer, nil) mapper, err := caasrbacmapper.NewMapper(loggo.Logger{}, m.mockSAInformer) c.Assert(err, jc.ErrorIsNil) @@ -140,7 +140,7 @@ func (m *MapperSuite) TestRBACMapperUpdateSync(c *gc.C) { return sa, nil }) - eventHandlers.OnAdd(sa) + eventHandlers.OnAdd(sa, false) waitGroup.Wait() for a := coretesting.LongAttempt.Start(); a.Next(); { @@ -193,7 +193,7 @@ func (m *MapperSuite) TestRBACMapperDeleteSync(c *gc.C) { DoAndReturn(func(h cache.ResourceEventHandlerFuncs) { eventHandlers = h waitGroup.Done() - }) + }).Return(m.mockSharedIndexInformer, nil) mapper, err := caasrbacmapper.NewMapper(loggo.Logger{}, m.mockSAInformer) c.Assert(err, jc.ErrorIsNil) @@ -215,7 +215,7 @@ func (m *MapperSuite) TestRBACMapperDeleteSync(c *gc.C) { m.mockSALister.EXPECT().ServiceAccounts(gomock.Eq(namespace)). Return(m.mockSANamespaceLister).AnyTimes() m.mockSANamespaceLister.EXPECT().Get(gomock.Eq(name)).Return(sa, nil) - eventHandlers.OnAdd(sa) + eventHandlers.OnAdd(sa, false) for a := coretesting.LongAttempt.Start(); a.Next(); { rAppName, err := mapper.AppNameForServiceAccount(uid) diff --git a/worker/migrationmaster/worker_test.go b/worker/migrationmaster/worker_test.go index 7a75b9db154..50a71572f94 100644 --- a/worker/migrationmaster/worker_test.go +++ b/worker/migrationmaster/worker_test.go @@ -228,7 +228,7 @@ func (s *Suite) TestSuccessfulMigration(c *gc.C) { // Observe that the migration was seen, the model exported, an API // connection to the target controller was made, the model was // imported and then the migration completed. - s.stub.CheckCalls(c, joinCalls( + assertExpectedCallArgs(c, s.stub, joinCalls( // Wait for migration to start. watchStatusLockdownCalls, []jujutesting.StubCall{ @@ -457,7 +457,8 @@ func (s *Suite) TestQUIESCEFailedAgent(c *gc.C) { }) s.checkWorkerReturns(c, migrationmaster.ErrInactive) - s.stub.CheckCalls(c, joinCalls( + + expectedCalls := joinCalls( watchStatusLockdownCalls, []jujutesting.StubCall{ {"facade.MinionReportTimeout", nil}, @@ -468,7 +469,9 @@ func (s *Suite) TestQUIESCEFailedAgent(c *gc.C) { {"facade.MinionReports", nil}, }, abortCalls, - )) + ) + + assertExpectedCallArgs(c, s.stub, expectedCalls) } func (s *Suite) TestQUIESCEWrongController(c *gc.C) { @@ -525,7 +528,7 @@ func (s *Suite) TestQUIESCETargetChecksFail(c *gc.C) { s.connection.prechecksErr = errors.New("boom") s.checkWorkerReturns(c, migrationmaster.ErrInactive) - s.stub.CheckCalls(c, joinCalls( + assertExpectedCallArgs(c, s.stub, joinCalls( watchStatusLockdownCalls, []jujutesting.StubCall{ {"facade.MinionReportTimeout", nil}, @@ -1141,6 +1144,27 @@ func (s *Suite) checkMinionWaitGetError(c *gc.C, phase coremigration.Phase) { s.checkWorkerErr(c, "boom") } +// assertExpectedCallArgs checks that the stub has been called with the +// expected arguments. It ignores the facade versions map on the Prechecks +// call because that's an implementation detail of the api facade, not the +// worker. As long as it's non-zero, otherwise we don't care. +func assertExpectedCallArgs(c *gc.C, stub *jujutesting.Stub, expectedCalls []jujutesting.StubCall) { + stub.CheckCallNames(c, callNames(expectedCalls)...) + for i, call := range expectedCalls { + stubCall := stub.Calls()[i] + + if call.FuncName == "MigrationTarget.Prechecks" { + mc := jc.NewMultiChecker() + mc.AddExpr("_.FacadeVersions", gc.Not(gc.HasLen), 0) + + c.Assert(stubCall.Args, mc, call.Args, gc.Commentf("call %s", call.FuncName)) + continue + } + + c.Assert(stubCall, jc.DeepEquals, call, gc.Commentf("call %s", call.FuncName)) + } +} + func stubCallNames(stub *jujutesting.Stub) []string { var out []string for _, call := range stub.Calls() { @@ -1509,6 +1533,14 @@ func joinCalls(allCalls ...[]jujutesting.StubCall) (out []jujutesting.StubCall) return } +func callNames(calls []jujutesting.StubCall) []string { + var out []string + for _, call := range calls { + out = append(out, call.FuncName) + } + return out +} + func makeMinionReports(p coremigration.Phase) coremigration.MinionReports { return coremigration.MinionReports{ MigrationId: "model-uuid:2", diff --git a/worker/trace/client.go b/worker/trace/client.go new file mode 100644 index 00000000000..b7187b4a54e --- /dev/null +++ b/worker/trace/client.go @@ -0,0 +1,168 @@ +// Copyright 2023 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package trace + +import ( + "context" + + "github.com/juju/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" + "go.opentelemetry.io/otel/trace" + + coretrace "github.com/juju/juju/core/trace" + "github.com/juju/juju/version" +) + +// This file solely exists so that we do not tie ourselves to OTEL directly +// into Juju. This allows us to swap out the tracing implementation in the +// future if we need to. I've retooled a codebase from Jaeger to opentracing +// and this lesson was learned the hard way. + +// Client manages connections to the collector, handles the +// transformation of data into wire format, and the transmission of that +// data to the collector. +type Client interface { + // Start should establish connection(s) to endpoint(s). It is + // called just once by the exporter, so the implementation + // does not need to worry about idempotence and locking. + Start(ctx context.Context) error + + // Stop should close the connections. The function is called + // only once by the exporter, so the implementation does not + // need to worry about idempotence, but it may be called + // concurrently with UploadTraces, so proper + // locking is required. The function serves as a + // synchronization point - after the function returns, the + // process of closing connections is assumed to be finished. + Stop(ctx context.Context) error +} + +// ClientSpan is directly equivalent to the opentelemetry Span interface, minus +// the embedded interface. +type ClientSpan interface { + // End completes the Span. The Span is considered complete and ready to be + // delivered through the rest of the telemetry pipeline after this method + // is called. Therefore, updates to the Span are not allowed after this + // method has been called. + End(options ...trace.SpanEndOption) + + // AddEvent adds an event with the provided name and options. + AddEvent(name string, options ...trace.EventOption) + + // IsRecording returns the recording state of the Span. It will return + // true if the Span is active and events can be recorded. + IsRecording() bool + + // RecordError will record err as an exception span event for this span. An + // additional call to SetStatus is required if the Status of the Span should + // be set to Error, as this method does not change the Span status. If this + // span is not being recorded or err is nil then this method does nothing. + RecordError(err error, options ...trace.EventOption) + + // SpanContext returns the SpanContext of the Span. The returned SpanContext + // is usable even after the End method has been called for the Span. + SpanContext() trace.SpanContext + + // SetStatus sets the status of the Span in the form of a code and a + // description, provided the status hasn't already been set to a higher + // value before (OK > Error > Unset). The description is only included in a + // status when the code is for an error. + SetStatus(code codes.Code, description string) + + // SetName sets the Span name. + SetName(name string) + + // SetAttributes sets kv as attributes of the Span. If a key from kv + // already exists for an attribute of the Span it will be overwritten with + // the value contained in kv. + SetAttributes(kv ...attribute.KeyValue) + + // TracerProvider returns a TracerProvider that can be used to generate + // additional Spans on the same telemetry pipeline as the current Span. + TracerProvider() trace.TracerProvider +} + +// Tracer is the creator of Spans. +type ClientTracer interface { + // Start creates a span and a context.Context containing the newly-created span. + // + // If the context.Context provided in `ctx` contains a Span then the newly-created + // Span will be a child of that span, otherwise it will be a root span. This behavior + // can be overridden by providing `WithNewRoot()` as a SpanOption, causing the + // newly-created Span to be a root span even if `ctx` contains a Span. + // + // When creating a Span it is recommended to provide all known span attributes using + // the `WithAttributes()` SpanOption as samplers will only have access to the + // attributes provided when a Span is created. + // + // Any Span that is created MUST also be ended. This is the responsibility of the user. + // Implementations of this API may leak memory or other resources if Spans are not ended. + Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, ClientSpan) +} + +// ClientTracerProvider is the interface for a tracer provider. +type ClientTracerProvider interface { + ForceFlush(ctx context.Context) error + Shutdown(ctx context.Context) error +} + +// NewClient returns a new tracing client. +func NewClient(ctx context.Context, namespace coretrace.TaggedTracerNamespace, endpoint string, insecureSkipVerify bool, sampleRatio float64) (Client, ClientTracerProvider, ClientTracer, error) { + options := []otlptracegrpc.Option{ + otlptracegrpc.WithEndpoint(endpoint), + } + if insecureSkipVerify { + options = append(options, otlptracegrpc.WithInsecure()) + } + + client := otlptracegrpc.NewClient(options...) + exporter, err := otlptrace.New(ctx, client) + if err != nil { + return nil, nil, nil, errors.Trace(err) + } + + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.TraceIDRatioBased(sampleRatio)), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource(newResource(namespace.ServiceName())), + ) + return client, tp, clientSpanShim{tp.Tracer(namespace.String())}, nil +} + +// clientSpanShim exists to mask out the embedded interface within the +// trace.Span +type clientSpanShim struct { + tracer trace.Tracer +} + +// Start creates a span and a context.Context containing the newly-created span. +// +// If the context.Context provided in `ctx` contains a Span then the newly-created +// Span will be a child of that span, otherwise it will be a root span. This behavior +// can be overridden by providing `WithNewRoot()` as a SpanOption, causing the +// newly-created Span to be a root span even if `ctx` contains a Span. +// +// When creating a Span it is recommended to provide all known span attributes using +// the `WithAttributes()` SpanOption as samplers will only have access to the +// attributes provided when a Span is created. +// +// Any Span that is created MUST also be ended. This is the responsibility of the user. +// Implementations of this API may leak memory or other resources if Spans are not ended. +func (s clientSpanShim) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, ClientSpan) { + return s.tracer.Start(ctx, spanName, opts...) +} + +func newResource(serviceName string) *resource.Resource { + return resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName(serviceName), + semconv.ServiceVersion(version.Current.String()), + ) +} diff --git a/worker/trace/package_test.go b/worker/trace/package_test.go index e47dba12916..db9b786fc62 100644 --- a/worker/trace/package_test.go +++ b/worker/trace/package_test.go @@ -11,7 +11,7 @@ import ( "github.com/go-logr/logr" jujutesting "github.com/juju/testing" "go.opentelemetry.io/otel" - trace "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace" "go.uber.org/mock/gomock" gc "gopkg.in/check.v1" @@ -76,7 +76,7 @@ func (s *baseSuite) expectClient() { s.client.EXPECT().Start(gomock.Any()).AnyTimes() s.client.EXPECT().Stop(gomock.Any()).AnyTimes() - s.clientTracer.EXPECT().Start(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + s.clientTracer.EXPECT().Start(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, ClientSpan) { return ctx, s.span }).AnyTimes() diff --git a/worker/trace/trace_mock_test.go b/worker/trace/trace_mock_test.go index ea8edc5eb5a..6fd35f2c2c0 100644 --- a/worker/trace/trace_mock_test.go +++ b/worker/trace/trace_mock_test.go @@ -167,3 +167,15 @@ func (mr *MockSpanMockRecorder) TracerProvider() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TracerProvider", reflect.TypeOf((*MockSpan)(nil).TracerProvider)) } + +// span mocks base method. +func (m *MockSpan) span() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "span") +} + +// span indicates an expected call of span. +func (mr *MockSpanMockRecorder) span() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "span", reflect.TypeOf((*MockSpan)(nil).span)) +} diff --git a/worker/trace/tracer.go b/worker/trace/tracer.go index cf278539558..12159192f5e 100644 --- a/worker/trace/tracer.go +++ b/worker/trace/tracer.go @@ -10,61 +10,12 @@ import ( "github.com/juju/errors" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.20.0" "go.opentelemetry.io/otel/trace" "gopkg.in/tomb.v2" coretrace "github.com/juju/juju/core/trace" - "github.com/juju/juju/version" ) -// Client manages connections to the collector, handles the -// transformation of data into wire format, and the transmission of that -// data to the collector. -type Client interface { - // Start should establish connection(s) to endpoint(s). It is - // called just once by the exporter, so the implementation - // does not need to worry about idempotence and locking. - Start(ctx context.Context) error - - // Stop should close the connections. The function is called - // only once by the exporter, so the implementation does not - // need to worry about idempotence, but it may be called - // concurrently with UploadTraces, so proper - // locking is required. The function serves as a - // synchronization point - after the function returns, the - // process of closing connections is assumed to be finished. - Stop(ctx context.Context) error -} - -// Tracer is the creator of Spans. -type ClientTracer interface { - // Start creates a span and a context.Context containing the newly-created span. - // - // If the context.Context provided in `ctx` contains a Span then the newly-created - // Span will be a child of that span, otherwise it will be a root span. This behavior - // can be overridden by providing `WithNewRoot()` as a SpanOption, causing the - // newly-created Span to be a root span even if `ctx` contains a Span. - // - // When creating a Span it is recommended to provide all known span attributes using - // the `WithAttributes()` SpanOption as samplers will only have access to the - // attributes provided when a Span is created. - // - // Any Span that is created MUST also be ended. This is the responsibility of the user. - // Implementations of this API may leak memory or other resources if Spans are not ended. - Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) -} - -// ClientTracerProvider is the interface for a tracer provider. -type ClientTracerProvider interface { - ForceFlush(ctx context.Context) error - Shutdown(ctx context.Context) error -} - // NewClientFunc is the function signature for creating a new client. type NewClientFunc func(context.Context, coretrace.TaggedTracerNamespace, string, bool, float64) (Client, ClientTracerProvider, ClientTracer, error) @@ -131,7 +82,7 @@ func (t *tracer) Start(ctx context.Context, name string, opts ...coretrace.Optio // they also die at the same time as the worker. var ( cancel context.CancelFunc - span trace.Span + span ClientSpan ) ctx = t.buildRequestContext(ctx) ctx, cancel = t.scopedContext(ctx) @@ -245,39 +196,8 @@ func (t *tracer) buildRequestContext(ctx context.Context) context.Context { return trace.ContextWithRemoteSpanContext(ctx, sc) } -// NewClient returns a new tracing client. -func NewClient(ctx context.Context, namespace coretrace.TaggedTracerNamespace, endpoint string, insecureSkipVerify bool, sampleRatio float64) (Client, ClientTracerProvider, ClientTracer, error) { - options := []otlptracegrpc.Option{ - otlptracegrpc.WithEndpoint(endpoint), - } - if insecureSkipVerify { - options = append(options, otlptracegrpc.WithInsecure()) - } - - client := otlptracegrpc.NewClient(options...) - exporter, err := otlptrace.New(ctx, client) - if err != nil { - return nil, nil, nil, errors.Trace(err) - } - - tp := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.TraceIDRatioBased(sampleRatio)), - sdktrace.WithBatcher(exporter), - sdktrace.WithResource(newResource(namespace.ServiceName())), - ) - return client, tp, tp.Tracer(namespace.String()), nil -} - -func newResource(serviceName string) *resource.Resource { - return resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceName(serviceName), - semconv.ServiceVersion(version.Current.String()), - ) -} - type managedSpan struct { - span trace.Span + span ClientSpan cancel context.CancelFunc scope coretrace.Scope stackTracesEnabled bool @@ -324,7 +244,7 @@ func (s *managedSpan) End(attrs ...coretrace.Attribute) { } type managedScope struct { - span trace.Span + span ClientSpan } // TraceID returns the trace ID of the span. diff --git a/worker/trace/tracer_mock_test.go b/worker/trace/tracer_mock_test.go index e79c29e458a..118ca02433c 100644 --- a/worker/trace/tracer_mock_test.go +++ b/worker/trace/tracer_mock_test.go @@ -9,7 +9,7 @@ import ( reflect "reflect" trace "github.com/juju/juju/core/trace" - trace0 "go.opentelemetry.io/otel/trace" + trace1 "go.opentelemetry.io/otel/trace" gomock "go.uber.org/mock/gomock" ) @@ -171,7 +171,7 @@ func (m *MockClientTracer) EXPECT() *MockClientTracerMockRecorder { } // Start mocks base method. -func (m *MockClientTracer) Start(arg0 context.Context, arg1 string, arg2 ...trace0.SpanStartOption) (context.Context, trace0.Span) { +func (m *MockClientTracer) Start(arg0 context.Context, arg1 string, arg2 ...trace1.SpanStartOption) (context.Context, ClientSpan) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { @@ -179,7 +179,7 @@ func (m *MockClientTracer) Start(arg0 context.Context, arg1 string, arg2 ...trac } ret := m.ctrl.Call(m, "Start", varargs...) ret0, _ := ret[0].(context.Context) - ret1, _ := ret[1].(trace0.Span) + ret1, _ := ret[1].(ClientSpan) return ret0, ret1 } diff --git a/worker/uniter/runner/context/context.go b/worker/uniter/runner/context/context.go index 9a68ba356ce..c6df0387de1 100644 --- a/worker/uniter/runner/context/context.go +++ b/worker/uniter/runner/context/context.go @@ -823,27 +823,28 @@ func (ctx *HookContext) GetSecret(uri *coresecrets.URI, label string, refresh, p if uri == nil && label == "" { return nil, errors.NotValidf("empty URI and label") } + if uri != nil { + if v, got := ctx.getPendingSecretValue(uri, label, refresh, peek); got { + return v, nil + } + } if label != "" { + if v, got := ctx.getPendingSecretValue(nil, label, refresh, peek); got { + return v, nil + } + } + if uri == nil && label != "" { // try to resolve label to URI by looking up owned secrets. ownedSecretURI, err := ctx.lookupOwnedSecretURIByLabel(label) if err != nil && !errors.Is(err, errors.NotFound) { return nil, err } if ownedSecretURI != nil { - if uri != nil { - return nil, errors.NewNotValid(nil, "either URI or label should be used for getting an owned secret but not both") - } - if refresh { - return nil, errors.NewNotValid(nil, "secret owner cannot use --refresh") - } // Found owned secret, no need label anymore. uri = ownedSecretURI label = "" } } - if v, got := ctx.getPendingSecretValue(uri); got { - return v, nil - } backend, err := ctx.getSecretsBackend() if err != nil { return nil, err @@ -855,18 +856,46 @@ func (ctx *HookContext) GetSecret(uri *coresecrets.URI, label string, refresh, p return v, nil } -func (ctx *HookContext) getPendingSecretValue(uri *coresecrets.URI) (coresecrets.SecretValue, bool) { - if uri == nil { +func (ctx *HookContext) getPendingSecretValue(uri *coresecrets.URI, label string, refresh, peek bool) (coresecrets.SecretValue, bool) { + if uri == nil && label == "" { return nil, false } - for _, v := range ctx.secretChanges.pendingCreates { - if v.URI != nil && v.URI.ID == uri.ID { + for i, v := range ctx.secretChanges.pendingCreates { + if uri != nil && v.URI != nil && v.URI.ID == uri.ID { + if label != "" { + pending := ctx.secretChanges.pendingCreates[i] + pending.Label = &label + ctx.secretChanges.pendingCreates[i] = pending + } + // The initial value of the secret is not stored in the database yet. + return v.Value, true + } + if label != "" && v.Label != nil && label == *v.Label { // The initial value of the secret is not stored in the database yet. return v.Value, true } } - for _, v := range ctx.secretChanges.pendingUpdates { - if v.URI != nil && v.URI.ID == uri.ID { + if !refresh && !peek { + return nil, false + } + + for i, v := range ctx.secretChanges.pendingUpdates { + if uri != nil && v.URI != nil && v.URI.ID == uri.ID { + if label != "" { + pending := ctx.secretChanges.pendingUpdates[i] + pending.Label = &label + ctx.secretChanges.pendingUpdates[i] = pending + } + if refresh { + ctx.secretChanges.pendingTrackLatest[v.URI.ID] = true + } + // The new value of the secret is going to be updated to the database. + return v.Value, v.Value != nil && !v.Value.IsEmpty() + } + if label != "" && v.Label != nil && label == *v.Label { + if refresh { + ctx.secretChanges.pendingTrackLatest[v.URI.ID] = true + } // The new value of the secret is going to be updated to the database. return v.Value, v.Value != nil && !v.Value.IsEmpty() } @@ -1434,12 +1463,13 @@ func (ctx *HookContext) doFlush(process string) error { } var ( - cleanups []coresecrets.ValueRef - pendingCreates []uniter.SecretCreateArg - pendingUpdates []uniter.SecretUpsertArg - pendingDeletes []uniter.SecretDeleteArg - pendingGrants []uniter.SecretGrantRevokeArgs - pendingRevokes []uniter.SecretGrantRevokeArgs + cleanups []coresecrets.ValueRef + pendingCreates []uniter.SecretCreateArg + pendingUpdates []uniter.SecretUpsertArg + pendingDeletes []uniter.SecretDeleteArg + pendingGrants []uniter.SecretGrantRevokeArgs + pendingRevokes []uniter.SecretGrantRevokeArgs + pendingTrackLatest []string ) for _, c := range ctx.secretChanges.pendingCreates { ref, err := secretsBackend.SaveContent(c.URI, 1, c.Value) @@ -1507,11 +1537,16 @@ func (ctx *HookContext) doFlush(process string) error { pendingRevokes = append(pendingRevokes, r) } + for uri := range ctx.secretChanges.pendingTrackLatest { + pendingTrackLatest = append(pendingTrackLatest, uri) + } + b.AddSecretCreates(pendingCreates) b.AddSecretUpdates(pendingUpdates) b.AddSecretDeletes(pendingDeletes) b.AddSecretGrants(pendingGrants) b.AddSecretRevokes(pendingRevokes) + b.AddTrackLatest(pendingTrackLatest) // Generate change request but skip its execution if no changes are pending. commitReq, numChanges := b.Build() diff --git a/worker/uniter/runner/context/context_test.go b/worker/uniter/runner/context/context_test.go index 2ab9662c87a..ff4749a7694 100644 --- a/worker/uniter/runner/context/context_test.go +++ b/worker/uniter/runner/context/context_test.go @@ -1117,6 +1117,7 @@ func (s *HookContextSuite) TestMissingAction(c *gc.C) { } func (s *HookContextSuite) assertSecretGetFromPendingChanges(c *gc.C, + refresh, peek bool, setPendingSecretChanges func(hc *context.HookContext, uri *coresecrets.URI, label string, value map[string]string), ) { defer s.setupMocks(c).Finish() @@ -1126,16 +1127,19 @@ func (s *HookContextSuite) assertSecretGetFromPendingChanges(c *gc.C, uri := coresecrets.NewURI() label := "label" data := map[string]string{"foo": "bar"} + if !refresh && !peek { + data["foo"] = "existing" + } setPendingSecretChanges(hookContext, uri, label, data) - context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, mockBackendClient{}) - value, err := hookContext.GetSecret(nil, label, false, false) + value, err := hookContext.GetSecret(nil, label, refresh, peek) c.Assert(err, jc.ErrorIsNil) c.Assert(value.EncodedValues(), jc.DeepEquals, data) } func (s *HookContextSuite) TestSecretGetFromPendingCreateChanges(c *gc.C) { - s.assertSecretGetFromPendingChanges(c, + s.assertSecretGetFromPendingChanges(c, false, false, func(hc *context.HookContext, uri *coresecrets.URI, label string, value map[string]string) { arg := apiuniter.SecretCreateArg{OwnerTag: s.mockUnit.Tag()} arg.URI = uri @@ -1148,7 +1152,7 @@ func (s *HookContextSuite) TestSecretGetFromPendingCreateChanges(c *gc.C) { } func (s *HookContextSuite) TestAppSecretGetFromPendingCreateChanges(c *gc.C) { - s.assertSecretGetFromPendingChanges(c, + s.assertSecretGetFromPendingChanges(c, false, true, func(hc *context.HookContext, uri *coresecrets.URI, label string, value map[string]string) { arg := uniter.SecretCreateArg{OwnerTag: names.NewApplicationTag(s.mockUnit.ApplicationName())} arg.URI = uri @@ -1161,7 +1165,7 @@ func (s *HookContextSuite) TestAppSecretGetFromPendingCreateChanges(c *gc.C) { } func (s *HookContextSuite) TestSecretGetFromPendingUpdateChanges(c *gc.C) { - s.assertSecretGetFromPendingChanges(c, + s.assertSecretGetFromPendingChanges(c, false, true, func(hc *context.HookContext, uri *coresecrets.URI, label string, value map[string]string) { arg := apiuniter.SecretUpdateArg{} arg.URI = uri @@ -1184,6 +1188,14 @@ func (mockBackend) GetContent(_ stdcontext.Context, revisionId string) (coresecr return coresecrets.NewSecretValue(map[string]string{"foo": "bar"}), nil } +type mockBackendClient struct { + secrets.BackendsClient +} + +func (mockBackendClient) GetContent(uri *coresecrets.URI, label string, refresh, peek bool) (coresecrets.SecretValue, error) { + return coresecrets.NewSecretValue(map[string]string{"foo": "existing"}), nil +} + func (s *HookContextSuite) TestSecretGet(c *gc.C) { ctrl := s.setupMocks(c) defer ctrl.Finish() @@ -1242,34 +1254,6 @@ func (s *HookContextSuite) TestSecretGet(c *gc.C) { }) } -func (s *HookContextSuite) TestSecretGetOwnedSecretFailedBothURIAndLabel(c *gc.C) { - defer s.setupMocks(c).Finish() - - uri := coresecrets.NewURI() - hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) - context.SetEnvironmentHookContextSecret(hookContext, uri.String(), - map[string]jujuc.SecretMetadata{ - uri.ID: {Label: "label", Owner: s.mockUnit.Tag()}, - }, nil, nil) - - _, err := hookContext.GetSecret(uri, "label", false, false) - c.Assert(err, gc.ErrorMatches, `either URI or label should be used for getting an owned secret but not both`) -} - -func (s *HookContextSuite) TestSecretGetOwnedSecretFailedWithUpdate(c *gc.C) { - defer s.setupMocks(c).Finish() - - uri := coresecrets.NewURI() - hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) - context.SetEnvironmentHookContextSecret(hookContext, uri.String(), - map[string]jujuc.SecretMetadata{ - uri.ID: {Label: "label", Owner: s.mockUnit.Tag()}, - }, nil, nil) - - _, err := hookContext.GetSecret(nil, "label", true, false) - c.Assert(err, gc.ErrorMatches, `secret owner cannot use --refresh`) -} - func (s *HookContextSuite) assertSecretGetOwnedSecretURILookup( c *gc.C, patchContext func(*context.HookContext, *coresecrets.URI, string, api.SecretsAccessor, secrets.BackendsClient), ) { @@ -1338,6 +1322,54 @@ func (s *HookContextSuite) TestSecretGetOwnedSecretURILookupFromPendingCreate(c ) } +func (s *HookContextSuite) TestSecretGetOwnedSecretLabelLookupFromPendingCreates(c *gc.C) { + defer s.setupMocks(c).Finish() + + hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) + uri := coresecrets.NewURI() + label := "label-" + uri.String() + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + + arg := uniter.SecretCreateArg{OwnerTag: s.mockUnit.Tag()} + arg.URI = uri + arg.Label = ptr(label) + arg.Value = coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) + hookContext.SetPendingSecretCreates( + map[string]uniter.SecretCreateArg{uri.ID: arg}) + + value, err := hookContext.GetSecret(nil, label, false, false) + c.Assert(err, jc.ErrorIsNil) + c.Assert(value.EncodedValues(), jc.DeepEquals, map[string]string{ + "foo": "bar", + }) +} + +func (s *HookContextSuite) TestSecretGetOwnedSecretUpdatePendingCreateLabel(c *gc.C) { + defer s.setupMocks(c).Finish() + + hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) + uri := coresecrets.NewURI() + label := "label-" + uri.String() + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + + arg := uniter.SecretCreateArg{OwnerTag: s.mockUnit.Tag()} + arg.URI = uri + arg.Label = ptr(label) + arg.Value = coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) + hookContext.SetPendingSecretCreates( + map[string]uniter.SecretCreateArg{uri.ID: arg}) + + value, err := hookContext.GetSecret(uri, "foobar", false, true) + c.Assert(err, jc.ErrorIsNil) + c.Assert(value.EncodedValues(), jc.DeepEquals, map[string]string{ + "foo": "bar", + }) + arg.Label = ptr("foobar") + c.Assert(hookContext.PendingSecretCreates(), jc.DeepEquals, map[string]uniter.SecretCreateArg{ + uri.ID: arg, + }) +} + func (s *HookContextSuite) TestSecretGetOwnedSecretURILookupFromPendingUpdate(c *gc.C) { s.assertSecretGetOwnedSecretURILookup(c, func(ctx *context.HookContext, uri *coresecrets.URI, label string, client api.SecretsAccessor, backend secrets.BackendsClient) { @@ -1351,6 +1383,78 @@ func (s *HookContextSuite) TestSecretGetOwnedSecretURILookupFromPendingUpdate(c ) } +func (s *HookContextSuite) TestSecretGetOwnedSecretLabelLookupFromPendingUpdatesPeek(c *gc.C) { + defer s.setupMocks(c).Finish() + + hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) + uri := coresecrets.NewURI() + label := "label-" + uri.String() + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + + arg := uniter.SecretUpdateArg{} + arg.URI = uri + arg.Label = ptr(label) + arg.Value = coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) + hookContext.SetPendingSecretUpdates( + map[string]uniter.SecretUpdateArg{uri.ID: arg}) + + value, err := hookContext.GetSecret(nil, label, false, true) + c.Assert(err, jc.ErrorIsNil) + c.Assert(value.EncodedValues(), jc.DeepEquals, map[string]string{ + "foo": "bar", + }) + c.Assert(hookContext.PendingSecretTrackLatest(), gc.HasLen, 0) +} + +func (s *HookContextSuite) TestSecretGetOwnedSecretLabelLookupFromPendingUpdatesRefresh(c *gc.C) { + defer s.setupMocks(c).Finish() + + hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) + uri := coresecrets.NewURI() + label := "label-" + uri.String() + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + + arg := uniter.SecretUpdateArg{} + arg.URI = uri + arg.Label = ptr(label) + arg.Value = coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) + hookContext.SetPendingSecretUpdates( + map[string]uniter.SecretUpdateArg{uri.ID: arg}) + + value, err := hookContext.GetSecret(nil, label, true, false) + c.Assert(err, jc.ErrorIsNil) + c.Assert(value.EncodedValues(), jc.DeepEquals, map[string]string{ + "foo": "bar", + }) + c.Assert(hookContext.PendingSecretTrackLatest(), jc.DeepEquals, map[string]bool{uri.ID: true}) +} + +func (s *HookContextSuite) TestSecretGetOwnedSecretUpdatePendingLabel(c *gc.C) { + defer s.setupMocks(c).Finish() + + hookContext := context.NewMockUnitHookContext(s.mockUnit, model.IAAS, s.mockLeadership) + uri := coresecrets.NewURI() + label := "label-" + uri.String() + context.SetEnvironmentHookContextSecret(hookContext, uri.String(), nil, nil, nil) + + arg := uniter.SecretUpdateArg{} + arg.URI = uri + arg.Label = ptr(label) + arg.Value = coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) + hookContext.SetPendingSecretUpdates( + map[string]uniter.SecretUpdateArg{uri.ID: arg}) + + value, err := hookContext.GetSecret(uri, "foobar", false, true) + c.Assert(err, jc.ErrorIsNil) + c.Assert(value.EncodedValues(), jc.DeepEquals, map[string]string{ + "foo": "bar", + }) + arg.Label = ptr("foobar") + c.Assert(hookContext.PendingSecretUpdates(), jc.DeepEquals, map[string]uniter.SecretUpdateArg{ + uri.ID: arg, + }) +} + func ptr[T any](v T) *T { return &v } diff --git a/worker/uniter/runner/context/export_test.go b/worker/uniter/runner/context/export_test.go index 4788e6fc22b..1b21f32ccc9 100644 --- a/worker/uniter/runner/context/export_test.go +++ b/worker/uniter/runner/context/export_test.go @@ -347,3 +347,7 @@ func (ctx *HookContext) PendingSecretGrants() map[string]uniter.SecretGrantRevok func (ctx *HookContext) PendingSecretRevokes() map[string]uniter.SecretGrantRevokeArgs { return ctx.secretChanges.pendingRevokes } + +func (ctx *HookContext) PendingSecretTrackLatest() map[string]bool { + return ctx.secretChanges.pendingTrackLatest +} diff --git a/worker/uniter/runner/context/secrets.go b/worker/uniter/runner/context/secrets.go index 2f6787e2c77..e825629cd2e 100644 --- a/worker/uniter/runner/context/secrets.go +++ b/worker/uniter/runner/context/secrets.go @@ -16,21 +16,23 @@ import ( type secretsChangeRecorder struct { logger loggo.Logger - pendingCreates map[string]uniter.SecretCreateArg - pendingUpdates map[string]uniter.SecretUpdateArg - pendingDeletes map[string]uniter.SecretDeleteArg - pendingGrants map[string]uniter.SecretGrantRevokeArgs - pendingRevokes map[string]uniter.SecretGrantRevokeArgs + pendingCreates map[string]uniter.SecretCreateArg + pendingUpdates map[string]uniter.SecretUpdateArg + pendingDeletes map[string]uniter.SecretDeleteArg + pendingGrants map[string]uniter.SecretGrantRevokeArgs + pendingRevokes map[string]uniter.SecretGrantRevokeArgs + pendingTrackLatest map[string]bool } func newSecretsChangeRecorder(logger loggo.Logger) *secretsChangeRecorder { return &secretsChangeRecorder{ - logger: logger, - pendingCreates: make(map[string]uniter.SecretCreateArg), - pendingUpdates: make(map[string]uniter.SecretUpdateArg), - pendingDeletes: make(map[string]uniter.SecretDeleteArg), - pendingGrants: make(map[string]uniter.SecretGrantRevokeArgs), - pendingRevokes: make(map[string]uniter.SecretGrantRevokeArgs), + logger: logger, + pendingCreates: make(map[string]uniter.SecretCreateArg), + pendingUpdates: make(map[string]uniter.SecretUpdateArg), + pendingDeletes: make(map[string]uniter.SecretDeleteArg), + pendingGrants: make(map[string]uniter.SecretGrantRevokeArgs), + pendingRevokes: make(map[string]uniter.SecretGrantRevokeArgs), + pendingTrackLatest: make(map[string]bool), } } @@ -79,6 +81,7 @@ func (s *secretsChangeRecorder) remove(uri *secrets.URI, revision *int) { delete(s.pendingUpdates, uri.ID) delete(s.pendingGrants, uri.ID) delete(s.pendingRevokes, uri.ID) + delete(s.pendingTrackLatest, uri.ID) s.pendingDeletes[uri.ID] = uniter.SecretDeleteArg{URI: uri, Revision: revision} }