Skip to content

Commit

Permalink
Merge pull request #494 from CircleCI-Public/add-remove-alias-command
Browse files Browse the repository at this point in the history
CIRCLE-29574: add a remove-alias command
  • Loading branch information
skimke authored Oct 23, 2020
2 parents 836fa18 + 164cb14 commit 7341e15
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
38 changes: 38 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,44 @@ func organizationNotFound(name string, vcs string) error {
return fmt.Errorf("the organization '%s' under '%s' VCS-type does not exist. Did you misspell the organization or VCS?", name, vcs)
}

func DeleteNamespaceAlias(cl *graphql.Client, name string) error {
var response struct{
DeleteNamespaceAlias struct{
Deleted bool
Errors GQLErrorsCollection
}
}
query := `
mutation($name: String!) {
deleteNamespaceAlias(name: $name) {
deleted
errors {
type
message
}
}
}
`
request := graphql.NewRequest(query)
request.SetToken(cl.Token)

request.Var("name", name)
err := cl.Run(request, &response)
if err != nil {
return err
}

if len(response.DeleteNamespaceAlias.Errors) > 0 {
return response.DeleteNamespaceAlias.Errors
}

if !response.DeleteNamespaceAlias.Deleted {
return errors.New("Namespace alias deletion failed for unknown reasons.")
}

return nil
}

// CreateNamespace creates (reserves) a namespace for an organization
func CreateNamespace(cl *graphql.Client, name string, organizationName string, organizationVcs string) (*CreateNamespaceResponse, error) {
getOrgResponse, getOrgError := getOrganization(cl, organizationName, organizationVcs)
Expand Down
36 changes: 36 additions & 0 deletions cmd/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ func newNamespaceCommand(config *settings.Config) *cobra.Command {
Short: "Operate on namespaces",
}

deleteAliasCmd := &cobra.Command{
Use: "delete-alias <name>",
Short: "(Server only) Delete a namespace alias",
Long: `Delete a namespace alias.
A namespace can have multiple aliases (names). This command deletes an alias left behind by a rename. The most recent alias cannot be deleted.
Example:
- namespace A is renamed to B
- alias B is created, coexisting with alias A
- after migrating config accordingly, we can delete the A alias.
This requires install admin privileges, because it will break existing builds using the deleted alias.`,
PreRunE: func(_ *cobra.Command, args []string) error {
opts.args = args
opts.cl = graphql.NewClient(config.Host, config.Endpoint, config.Token, config.Debug)

return validateToken(opts.cfg)
},
RunE: func(_ *cobra.Command, _ []string) error {
return deleteNamespaceAlias(opts)
},
Args: cobra.ExactArgs(1),
Annotations: make(map[string]string),
Hidden: true,
}

deleteAliasCmd.Annotations["<name>"] = "The name of the alias to delete"

createCmd := &cobra.Command{
Use: "create <name> <vcs-type> <org-name>",
Short: "Create a namespace",
Expand Down Expand Up @@ -109,12 +138,19 @@ Please note that at this time all namespaces created in the registry are world-r
renameCmd.Annotations["<old-name>"] = "The current name of the namespace"
renameCmd.Annotations["<new-name>"] = "The new name you want to give the namespace"

namespaceCmd.AddCommand(deleteAliasCmd)
namespaceCmd.AddCommand(createCmd)
namespaceCmd.AddCommand(renameCmd)

return namespaceCmd
}

func deleteNamespaceAlias(opts namespaceOptions) error {
aliasName := opts.args[0]
err := api.DeleteNamespaceAlias(opts.cl, aliasName)
return err
}

func createNamespace(opts namespaceOptions) error {
namespaceName := opts.args[0]

Expand Down
93 changes: 93 additions & 0 deletions cmd/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"os/exec"
"time"

"github.com/CircleCI-Public/circleci-cli/clitest"
. "github.com/onsi/ginkgo"
Expand All @@ -27,6 +28,98 @@ var _ = Describe("Namespace integration tests", func() {
tempSettings.Close()
})

Describe("deleting namespace aliases", func() {
BeforeEach(func() {
command = exec.Command(pathCLI,
"namespace",
"delete-alias",
"--skip-update-check",
"--token", token,
"--host", tempSettings.TestServer.URL(),
"foo-ns",
)
})

It("returns message for when deletion unexpectedly failed", func() {
gqlDeleteNsAliasResponse := `{
"deleteNamespaceAlias": {
"errors": [],
"deleted": false
}
}`
expectedDeleteNsAliasRequest := `{
"query": "\nmutation($name: String!) {\n deleteNamespaceAlias(name: $name) {\n deleted\n errors {\n type\n message\n }\n }\n}\n",
"variables": {
"name": "foo-ns"
}
}`

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Request: expectedDeleteNsAliasRequest,
Response: gqlDeleteNsAliasResponse})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Err).Should(gbytes.Say("Namespace alias deletion failed for unknown reasons."))
Eventually(session).ShouldNot(gexec.Exit(0))
})

It("returns all errors returned by the GraphQL API", func() {
gqlDeleteNsAliasResponse := `{
"deleteNamespaceAlias": {
"errors": [{"message": "error1"}],
"deleted": false
}
}`
expectedDeleteNsAliasRequest := `{
"query": "\nmutation($name: String!) {\n deleteNamespaceAlias(name: $name) {\n deleted\n errors {\n type\n message\n }\n }\n}\n",
"variables": {
"name": "foo-ns"
}
}`

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Request: expectedDeleteNsAliasRequest,
Response: gqlDeleteNsAliasResponse})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Err).Should(gbytes.Say("Error: error1"))
Eventually(session).ShouldNot(gexec.Exit(0))
})

It("works given an alias name", func() {
By("setting up a mock server")
gqlDeleteNsAliasResponse := `{
"deleteNamespaceAlias": {
"errors": [],
"deleted": true
}
}`
expectedDeleteNsAliasRequest := `{
"query": "\nmutation($name: String!) {\n deleteNamespaceAlias(name: $name) {\n deleted\n errors {\n type\n message\n }\n }\n}\n",
"variables": {
"name": "foo-ns"
}
}`

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Request: expectedDeleteNsAliasRequest,
Response: gqlDeleteNsAliasResponse})

By("running the command")
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session, time.Second * 5).Should(gexec.Exit(0))
})
})

Context("create, skipping prompts", func() {
Describe("registering a namespace", func() {
BeforeEach(func() {
Expand Down

0 comments on commit 7341e15

Please sign in to comment.