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

Tiller proxy basic functionality #357

Merged
merged 14 commits into from
Jun 21, 2018
Merged

Tiller proxy basic functionality #357

merged 14 commits into from
Jun 21, 2018

Conversation

andresmgot
Copy link
Contributor

@andresmgot andresmgot commented Jun 12, 2018

Closes #351
Closes #352
Closes #353

This PR implements the basic functionality for the Tiller proxy discussed in #337. The proxy currently redirect requests to Tiller to list, create, get, update and delete releases. The commits has been split for an easier review. There is no need to review 3f0d6e7 since the code there has been already reviewed in the helm-crd project. Part of 22d2777 has been extracted from that project as well.

The proxy uses the same packages to serve the API than the chart service: gorilla/mux for the router and urfave/negroni as a middleware.

Copy link
Contributor

@prydonius prydonius left a comment

Choose a reason for hiding this comment

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

This mostly LGTM, nice work.

Now that this is in this repo, there may be certain helpers we want to share with other services (e.g. chart-repo has helpers to deal with repo URLs and appending tarball URLs, downloading tarballs, etc.)

In addition to that, we should probably start putting packages in pkg directory and leave the cmd directory for main packages that import packages from pkg.

These are globally-scoped things that I don't want to scope-creep into this issue, so I'm okay with merging this without doing all that, but it would be good to tackle this at some point (or ideally at least some of it now before this gets merged).

@@ -0,0 +1,4 @@
FROM alpine:3.6
Copy link
Contributor

Choose a reason for hiding this comment

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

can we not run the binary directly, or with telepresence?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

true, I'm just not that used to telepresence :)


var (
settings environment.EnvSettings
p *proxy.Proxy
Copy link
Contributor

Choose a reason for hiding this comment

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

since this is a global var, can we call it proxy to make it more obvious what it is?


config, err := rest.InClusterConfig()
if err != nil {
panic(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

we should probably use log.Fatalf or similar instead of panic.


// GetRelease returns the info of a release
func (p *Proxy) GetRelease(name, namespace string) (*release.Release, error) {
lock(name)
Copy link
Contributor

Choose a reason for hiding this comment

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

since this is read-only, do we need the lock? I think we only care about e.g. deleting a release whilst it's still being created?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's not critical but it's something nice to have. We could be returning the wrong version or a missing release.

return &chart.Chart{}, nil
}

func prepareTestProxy(hrs []helmRelease, existingTillerReleases []AppOverview) *Proxy {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would name this newTestProxy or newFakeProxy - prepare doesn't sound like something that returns a *Proxy to me.

@andresmgot
Copy link
Contributor Author

In addition to that, we should probably start putting packages in pkg directory and leave the cmd directory for main packages that import packages from pkg.

I agree with that, I just followed the existing pattern but it's true. I will move at least the current extra packages to the pkg folder and leave cmd just to the command line related package.

@prydonius
Copy link
Contributor

I agree with that, I just followed the existing pattern but it's true. I will move at least the current extra packages to the pkg folder and leave cmd just to the command line related package.

Yeah that's a side effect of when we merged together the different repos, we didn't also move packages to the pkg folder, though we should have at the time. Moving the current extra packages sounds great, thanks!

Copy link
Contributor

@prydonius prydonius left a comment

Choose a reason for hiding this comment

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

Can we move the packages from pkg/utils/proxy|chart to just pkg/proxy|chart?

Otherwise LGTM

@andresmgot
Copy link
Contributor Author

andresmgot commented Jun 18, 2018

@prydonius I have updated this PR to include the authorization feature (creating a new PR would be confusing since I needed to reorder the code of this PR). From the last bunch of commits the interesting ones for review are 6d2dc61 which contains all the auth related code and
0be05b7 that contains the wiring code for the REST server. Sorry for the big PR.


# Enabling authorization

It is possible to enable authorization for helm releases setting the env var `ENABLE_AUTH`. If enabled, the client should have permissions to:
Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps ENABLE_RBAC is more clear? Also, IMO this should be enabled by default. I also thing this should be a command line flag rather than an env var.

Copy link
Contributor Author

@andresmgot andresmgot Jun 19, 2018

Choose a reason for hiding this comment

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

👍 I planned to do that the default when doing kubeapps up but it's true, a flag will be better. Also this will work (in theory) with any authorization system not just RBAC (e.g. ABAC): https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules

@@ -35,3 +29,14 @@ This proxy provides 6 different routes:
- `GET` `/namespaces/{namespace}/releases/{release}`: Get release info
- `PUT` `/namespaces/{namespace}/releases/{release}`: Update release info
- `DELETE` `/namespaces/{namespace}/releases/{release}`: Delete a release

# Enabling authorization
Copy link
Contributor

Choose a reason for hiding this comment

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

We should add a caveat that we only support bearer token auth and not other methods atm.

// WithParams can be used to wrap handlers to take an extra arg for path params
type WithParams func(http.ResponseWriter, *http.Request, Params)
// WithAuth can be used to wrap handlers to take an extra arg for path params
type WithAuth func(http.ResponseWriter, *http.Request, Params, *auth.UserAuth)
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO a more clean and modular way to implement this is to write middleware for Negroni and store the auth in the request context. The current approach ties together the auth and params. It's possible that WithParams could also be implemented as a middleware, but that's out of scope.

This is the middleware from Monocular: https://github.com/kubernetes-helm/monocular/blob/master/src/api/middleware/authgate.go, and this is an example of who it can be wrapped in a route: https://github.com/kubernetes-helm/monocular/blob/master/src/api/main.go#L102. The request context will need a key that can be used to read/set the auth data (https://github.com/kubernetes-helm/monocular/blob/master/src/api/models/user.go#L27), we may need a new package to share this.

return
}
action := "create"
if req.Method == "PUT" && params["releaseName"] != "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

why not use a different handler for PUT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it has just a couple of lines different but I guess it's better to have different handlers for readability

proxy.LogReleaseStatus(rel.Name)
status, err := proxy.GetReleaseStatus(rel.Name)
if err != nil {
log.Printf("Unable to fecth release status of %s: %v", rel.Name, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

s/fecth/fetch

pkg/auth/auth.go Outdated
Namespace string
}

// NewAuth creates a auth agent
Copy link
Contributor

Choose a reason for hiding this comment

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

s/a/an

pkg/auth/auth.go Outdated
if err != nil {
return err
}
default:
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can just have the default be err := u.isAllowed(action, resources and remove all other cases apart from upgrade. The SelfSubjectAccessReview will fail for unknown verbs anyway.

if !strings.Contains(err.Error(), "Unauthorized to create v1/pods in the bar namespace") {
t.Errorf("Unexpected error: %v", err)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

any way to test a successful request?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't find a way of doing so without modify the functional code in a weird way. For being able to return a "success" we need to add to the fake client a SelfSubjectAccessReviews object but since the functional code does a Create (because a Get doesn't make sense in the real scenario) it returns an error "already exists".

@prydonius
Copy link
Contributor

@andresmgot added some comments, some of which are hidden as "outdated" (although they aren't really)

@andresmgot
Copy link
Contributor Author

@prydonius reviewed/replied

Copy link
Contributor

@prydonius prydonius left a comment

Choose a reason for hiding this comment

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

lgtm

func logStatus(name string) {
status, err := proxy.GetReleaseStatus(name)
if err != nil {
log.Printf("Unable to fecth release status of %s: %v", name, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

s/fecth/fetch

@andresmgot andresmgot merged commit 2c70d7f into master Jun 21, 2018
@prydonius prydonius deleted the tillerProxy branch June 21, 2018 17:22
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.

2 participants