Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(service-version): Allow 'locked' services to be activated. #1245

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .tmpl/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type CreateCommand struct {
// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Manifest: c.manifest,
Expand Down
2 changes: 2 additions & 0 deletions .tmpl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type DeleteCommand struct {
// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Manifest: c.manifest,
Expand Down
1 change: 0 additions & 1 deletion .tmpl/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ type DescribeCommand struct {
// Exec invokes the application logic for the command.
func (c *DescribeCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
AllowActiveLocked: true,
Client: c.Globals.Client,
Manifest: c.manifest,
Out: out,
Expand Down
1 change: 0 additions & 1 deletion .tmpl/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ type ListCommand struct {
// Exec invokes the application logic for the command.
func (c *ListCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
AllowActiveLocked: true,
Client: c.Globals.Client,
Manifest: c.manifest,
Out: out,
Expand Down
38 changes: 31 additions & 7 deletions .tmpl/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@ func TestCreate(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--service-id 123 --version 1",
WantError: "service version 1 is not editable",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate Create${CLI_API} API error",
Expand Down Expand Up @@ -88,12 +96,20 @@ func TestDelete(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--service-id 123 --version 1",
WantError: "service version 1 is not editable",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate Delete${CLI_API} API error",
Expand Down Expand Up @@ -233,12 +249,20 @@ func TestUpdate(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--service-id 123 --version 1",
WantError: "service version 1 is not editable",
Args: "--name foobar --service-id 123 --version 1",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Args: "--name foobar --service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate Update${CLI_API} API error",
Expand Down
2 changes: 2 additions & 0 deletions .tmpl/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type UpdateCommand struct {
// Exec invokes the application logic for the command.
func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Manifest: c.manifest,
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,5 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require 4d63.com/optional v0.2.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
4d63.com/optional v0.2.0 h1:VtMa/Iy8Xn5JuIqJYwDScgBSBsZsKCwP7s35NiUB+8A=
4d63.com/optional v0.2.0/go.mod h1:DBA8tAdkYkYbvRq1lK3FyDBBzioAJzZzQPC6Vj+a3jk=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
Expand Down
43 changes: 39 additions & 4 deletions pkg/argparser/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/fastly/go-fastly/v9/fastly"
"github.com/fastly/kingpin"

"4d63.com/optional"
"github.com/fastly/cli/pkg/api"
"github.com/fastly/cli/pkg/env"
fsterr "github.com/fastly/cli/pkg/errors"
Expand Down Expand Up @@ -108,7 +109,14 @@ type OptionalFloat64 struct {
// ServiceDetailsOpts provides data and behaviours required by the
// ServiceDetails function.
type ServiceDetailsOpts struct {
AllowActiveLocked bool
// Active controls whether active serviceversions will be included in the result;
// if this is Empty, then the 'active' state of the version is ignored;
// otherwise, the 'active' state must match the value
Active optional.Optional[bool]
// Locked controls whether locked serviceversions will be included in the result;
// if this is Empty, then the 'locked' state of the version is ignored;
// otherwise, the 'locked' state must match the value
Locked optional.Optional[bool]
AutoCloneFlag OptionalAutoClone
APIClient api.Interface
Manifest manifest.Data
Expand Down Expand Up @@ -140,14 +148,41 @@ func ServiceDetails(opts ServiceDetailsOpts) (serviceID string, serviceVersion *
if err != nil {
return serviceID, currentVersion, err
}
} else if !opts.AllowActiveLocked && (fastly.ToValue(v.Active) || fastly.ToValue(v.Locked)) {
return serviceID, v, nil
}

failure := false
var failureState string

if active, present := opts.Active.Get(); present {
if active && !fastly.ToValue(v.Active) {
failure = true
failureState = "not active"
}
if !active && fastly.ToValue(v.Active) {
failure = true
failureState = "active"
}
}

if locked, present := opts.Locked.Get(); present {
if locked && !fastly.ToValue(v.Locked) {
failure = true
failureState = "not locked"
}
if !locked && fastly.ToValue(v.Locked) {
failure = true
failureState = "locked"
}
}

if failure {
err = fsterr.RemediationError{
Inner: fmt.Errorf("service version %d is not editable", fastly.ToValue(v.Number)),
Inner: fmt.Errorf("service version %d is %s", fastly.ToValue(v.Number), failureState),
Remediation: fsterr.AutoCloneRemediation,
}
return serviceID, v, err
}

return serviceID, v, nil
}

Expand Down
38 changes: 31 additions & 7 deletions pkg/commands/acl/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@ func TestACLCreate(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foo --service-id 123 --version 1",
WantError: "service version 1 is not editable",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foo --service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate CreateACL API error",
Expand Down Expand Up @@ -102,12 +110,20 @@ func TestACLDelete(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foobar --service-id 123 --version 1",
WantError: "service version 1 is not editable",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foo --service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate DeleteACL API error",
Expand Down Expand Up @@ -276,12 +292,20 @@ func TestACLUpdate(t *testing.T) {
WantError: "error reading service: no service ID found",
},
{
Name: "validate missing --autoclone flag",
Name: "validate missing --autoclone flag with 'active' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foo --new-name beepboop --service-id 123 --version 1",
WantError: "service version 1 is active",
},
{
Name: "validate missing --autoclone flag with 'locked' service",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
Arg: "--name foobar --new-name beepboop --service-id 123 --version 1",
WantError: "service version 1 is not editable",
Arg: "--name foo --new-name beepboop --service-id 123 --version 2",
WantError: "service version 2 is locked",
},
{
Name: "validate UpdateACL API error",
Expand Down
3 changes: 3 additions & 0 deletions pkg/commands/acl/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/fastly/go-fastly/v9/fastly"

"4d63.com/optional"
"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
Expand Down Expand Up @@ -64,6 +65,8 @@ type CreateCommand struct {
// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
APIClient: c.Globals.APIClient,
ErrLog: c.Globals.ErrLog,
Expand Down
3 changes: 3 additions & 0 deletions pkg/commands/acl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/fastly/go-fastly/v9/fastly"

"4d63.com/optional"
"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
Expand Down Expand Up @@ -63,6 +64,8 @@ type DeleteCommand struct {
// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
APIClient: c.Globals.APIClient,
Manifest: *c.Globals.Manifest,
Expand Down
1 change: 0 additions & 1 deletion pkg/commands/acl/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error {
}

serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
AllowActiveLocked: true,
APIClient: c.Globals.APIClient,
Manifest: *c.Globals.Manifest,
Out: out,
Expand Down
1 change: 0 additions & 1 deletion pkg/commands/acl/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error {
}

serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
AllowActiveLocked: true,
APIClient: c.Globals.APIClient,
Manifest: *c.Globals.Manifest,
Out: out,
Expand Down
3 changes: 3 additions & 0 deletions pkg/commands/acl/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/fastly/go-fastly/v9/fastly"

"4d63.com/optional"
"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
Expand Down Expand Up @@ -65,6 +66,8 @@ type UpdateCommand struct {
// Exec invokes the application logic for the command.
func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
APIClient: c.Globals.APIClient,
Manifest: *c.Globals.Manifest,
Expand Down
30 changes: 26 additions & 4 deletions pkg/commands/backend/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,31 @@ func TestBackendCreate(t *testing.T) {
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
WantError: "service version 1 is not editable",
WantError: "service version 1 is active",
},
// The following test is the same as the above but it appends --autoclone
// The following test specifies a service version that's 'locked', and
// subsequently we expect it to not be cloned as we don't provide the
// --autoclone flag and trying to add a backend to a locked service
// should cause an error.
{
Arg: "--service-id 123 --version 2 --address example.com --name www.test.com",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
},
WantError: "service version 2 is locked",
},
// The following test is the same as the 'active' test above but it appends --autoclone
// so we can be sure the backend creation error still occurs.
{
Arg: "--service-id 123 --version 1 --address example.com --name www.test.com --autoclone",
API: mock.API{
ListVersionsFn: testutil.ListVersions,
CloneVersionFn: testutil.CloneVersionResult(4),
CreateBackendFn: createBackendError,
},
WantError: errTest.Error(),
},
// The following test is the same as the 'locked' test above but it appends --autoclone
// so we can be sure the backend creation error still occurs.
{
Arg: "--service-id 123 --version 1 --address example.com --name www.test.com --autoclone",
Expand Down Expand Up @@ -122,8 +144,8 @@ func TestBackendCreate(t *testing.T) {
},
WantOutput: "Created backend www.test.com (service 123 version 4)",
},
// The following test specifies a service version that's 'inactive', and
// subsequently we expect it to be the same editable version.
// The following test specifies a service version that's 'inactive' and not 'locked',
// and subsequently we expect it to be the same editable version.
{
Arg: "--service-id 123 --version 3 --address 127.0.0.1 --name www.test.com",
API: mock.API{
Expand Down
3 changes: 3 additions & 0 deletions pkg/commands/backend/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/fastly/go-fastly/v9/fastly"

"4d63.com/optional"
"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
Expand Down Expand Up @@ -127,6 +128,8 @@ func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateComman
// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
Active: optional.Of(false),
Locked: optional.Of(false),
AutoCloneFlag: c.autoClone,
APIClient: c.Globals.APIClient,
Manifest: *c.Globals.Manifest,
Expand Down
Loading