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

Introduce POST /v3/roles endpoint to create space roles #215

Merged
merged 6 commits into from
Nov 9, 2021

Conversation

danail-branekov
Copy link
Member

Is there a related GitHub Issue?

#160

What is this change about?

Introduce the POST /v3/roles API endpoint. Whenever called, the API would create a rolebinding in a space namespace that binds a user to a role. Note that the API would fail to create a space role if the user does not have a role (i.e. a role binding) in the parent org namespace (this is how the API should behave according to the CF API documentation)

Does this PR introduce a breaking change?

Yes

K8s role names differ from the CF roles, for example, space_developer maps to cf-k8s-controllers-space-developer. In order to express this mapping we have introduced a new configuration yaml providing that mapping. We did not want to put that config in cf_k8s_api_config.yaml as that would mean that we would have to carry those mappings across all kustomization overlays.
That is why we now have two config files and we changed the meaning of the CONFIG environment variable to reference a directory, rather than a single file. The main would read the content of the directory and gradually fill the configuration object.
We have amended the .envrc accordingly so that make run works.

Acceptance Steps

  • Create org
    curl -X POST /v3/organizations -H 'Content-Type: application/json' -d '{"name":"my-org"}'

  • Create a rolebinding in the org namespaces that binds a user to whatever role
    kubectl create rolebinding <rolebinding-name> -n <org-ns-name> --clusterrole=some-role --user=some-user

  • Create space
    curl -X POST /v3/spaces -H 'Content-Type: application/json' -d '{ "name": "space-name", "relationships": { "organization": { "data": { "guid": "<org-guid>" } } } }'

  • Create the role:

curl "https://api.example.org/v3/roles" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
      "type": "cf-k8s-controllers-space-developer",
      "relationships": {
        "user": {
          "data": {
            "guid": "some-user>"
          }
        },
        "space": {
          "data": {
            "guid": "<the space GUID>"
          }
        }
      }
    }'

*See success response like:

{
   "guid": "40557c70-d1bd-4976-a2ab-a85f5e882418",
   "created_at": "2019-10-10T17:19:12Z",
   "updated_at": "2019-10-10T17:19:12Z",
   "type": "<the ClusterRole name>",
   "relationships": {
      "user": {
         "data": {
            "guid": "<the username>"
         }
      },
      "space": {
         "data": {
            "guid": "<the space GUID>"
         }
      },
      "organization": {
         "data": null
      }
   },
   "links": {
      "self": {
         "href": "https://api.example.org/v3/roles/40557c70-d1bd-4976-a2ab-a85f5e882418"
      },
      "space": {
         "href": "https://api.example.org/v3/spaces/05c5da3b-6cbc-421c-87c3-20bb3c41ab7c"
      }
   }
}

Tag your pair, your PM, and/or team

@cloudfoundry/cf-k8s

@danail-branekov danail-branekov force-pushed the wip-create-role branch 2 times, most recently from 33969d0 to 6699804 Compare November 5, 2021 15:12
Copy link
Contributor

@kieron-dev kieron-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See inline comments.

Also, where are the changes to the .envrc mentioned in one of the commits?

})
})

When("the request body is invalid with missing required name field", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/name/type

JustBeforeEach(func() {
namespaces, getErr = org.GetAuthorizedNamespaces(ctx, identity)
roleName1 = generateGUID("org-user-1")
roleName2 = generateGUID("org-user-1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/org-user-1/org-user-2/

It("lists the namespaces with bindings for current user", func() {
Expect(getErr).NotTo(HaveOccurred())
Expect(namespaces).To(ConsistOf(org1Ns))
Expect(k8sClient.Delete(context.Background(), &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: roleName1}})).To(Succeed())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use a ctx var above somewhere and replace all those context.Background() calls?

BeforeEach(func() {
ctx, cancelCtx = context.WithDeadline(ctx, time.Now().Add(-time.Minute))
BeforeEach(func() {
ctx, cancelCtx = context.WithDeadline(ctx, time.Now().Add(-time.Minute))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's not from this PR, but I really appreciate using an expired context to make k8s client calls fail :)

}

now := time.Now()
role.CreatedAt = now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the metadata.creationTimestamp from the k8s roleBinding instead of now?

})

Describe("CreateSpaceRole", func() {
createSubnamespaceAnchor := func(name, parent string) *hnsv1alpha2.SubnamespaceAnchor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we set SpaceNameLabel, this is creating a space subnamespaceanchor. Consider renaming.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also some duplication with org/space creation in the org_repository_test code. Potential refactor.


BeforeEach(func() {
authorizedInChecker.AuthorizedInReturns(true, nil)
orgAnchor = createSubnamespaceAnchor(uuid.NewString(), rootNamespace)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are creating an org anchor, but setting the SpaceNameLabel on it. Suggest modifying this to remove future confusion

@@ -59,6 +67,16 @@ var _ = BeforeSuite(func() {
ensureServerIsUp()
})

var _ = BeforeEach(func() {
serviceAccountName = generateGUID("user")
token := obtainServiceAccountToken(serviceAccountName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obtainServiceAccountToken does a fair bit of work, so we're introducing a significant overhead into each test here.

Consider whether this is necessary for every e2e test. If so, it might make more sense to be shared in a synchronised before suite.

userName string
)

createRoleWithHeaders := func(roleName, userName, spaceGUID string, headers map[string]string) (*http.Response, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a bit premature? We're currently using the privileged client to create the role binding, so the auth header is not used.

})

It("creates a role binding", func() {
response, err := createRoleWithHeaders("space_developer", userName, space.GUID, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil. See comment above.

danail-branekov and others added 6 commits November 9, 2021 12:04
* Currently supports only creating space roles
* Generate predictable role binding names by doing a sha256sum on the
  role name and user. The name is also prefixed with `cf-` to ensure it
  starts witha lowercase letter.
* The role will fail to create if the user does not have a role in the
  parent org namespace (as per the docs for POST /v3/roles)

Issue: #160

Co-authored-by: Mario Nitchev <marionitchev@gmail.com>
Co-authored-by: Kieron Browne <kbrowne@vmware.com>
We've seen an increased amount of flakes while trying to get the
ServiceAccount secret

Co-authored-by: Kieron Browne <kbrowne@vmware.com>
Co-authored-by: Mario Nitchev <marionitchev@gmail.com>
Although it doesn't work in GitHub Actions, it still saves time when
running locally

Co-authored-by: Kieron Browne <kbrowne@vmware.com>
* the api now parses all yaml files in a directory pointed by the CONFIG
  env var and merges them in a single config
* adjust the CONFIG in .envrc

Co-authored-by: Mario Nitchev <marionitchev@gmail.com>
Co-authored-by: Mario Nitchev <marionitchev@gmail.com>
Co-authored-by: Kieron Browne <kbrowne@vmware.com>
Co-authored-by: Mario Nitchev <marionitchev@gmail.com>
@kieron-dev kieron-dev merged commit 05065c1 into main Nov 9, 2021
@kieron-dev kieron-dev deleted the wip-create-role branch November 9, 2021 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: API client can assign a space role to a user with POST /v3/roles
3 participants