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

[Backend][Multi-user] Add namespace to Experiment API implementation #3243

Merged
merged 6 commits into from
Mar 17, 2020

Conversation

chensun
Copy link
Member

@chensun chensun commented Mar 9, 2020

Conduct user authorization on namespace for the following experiment APIs:

  • CreateExperiment
  • GetExperiment
  • ListExperiment
  • DeleteExperiment

/cc @IronPan @Bobgy

Image for testing: gcr.io/chesu-dev/api-server:pr3243


This change is Reviewable

@chensun
Copy link
Member Author

chensun commented Mar 12, 2020

/retest

@Bobgy
Copy link
Contributor

Bobgy commented Mar 13, 2020

/assign @Bobgy

@Bobgy
Copy link
Contributor

Bobgy commented Mar 13, 2020

Shall we add some tests when multi user mode is on for resource_manager_test, api_converter_test? Because there will be new code paths just for that.

Copy link
Contributor

@Bobgy Bobgy left a comment

Choose a reason for hiding this comment

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

Great work!
/lgtm

Now I realize you emphasized a lot on integration tests for the complete experiment server from end to end. I think that's a good practice, but it's different from existing pattern. It seems with existing pattern, we need some tests for each unit.
/cc @IronPan
for reviewing this part.

I'm okay with your choices because I don't have much context on which would be better and why it is like current convention.

backend/src/apiserver/server/api_converter.go Show resolved Hide resolved
backend/src/apiserver/resource/model_converter.go Outdated Show resolved Hide resolved
@@ -817,7 +821,7 @@ func (r *ResourceManager) CreateDefaultExperiment() (string, error) {
}

// Create default experiment
defaultExperiment := &model.Experiment{
defaultExperiment := &api.Experiment{
Name: "Default",
Description: "All runs created without specifying an experiment will be grouped here.",
}
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 no namespace reference here, so it will always fail in multi user mode.

Shall we add a check earlier? We just skip trying to create a default namespace when in multi user mode?

Copy link
Member Author

Choose a reason for hiding this comment

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

Great catch! Apparently this is one place I missed unit test coverage, because this default experiment creation doesn't hit experiment_server.
To allow creating default experiment in multi-user mode, I moved the namespace requirement out of ToModelExperiment -- it is enforced in experimentServer.CreateExperiment which is sufficient to capture invalid input. Also added unit tests for CreateDefaultExperiment and ToModelExperiment

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully remember your design about default experiment. If default experiment doesn't contain a namespace, it will be useless in multi user mode.

Maybe we should just throw an error message here for multi user mode like Please specify an experiment. No default experiment has been set up.?

Copy link
Member Author

Choose a reason for hiding this comment

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

The plan is:

  1. for short term, we provide a SQL script to help users backfill existing experiments including the default one.
  2. long term, we will automate the backfill in api-server using the namespace specified by a config map/environment variable.
    So allowing the default experiment without namespace is okay for now.

Copy link
Contributor

@Bobgy Bobgy left a comment

Choose a reason for hiding this comment

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

Now I understand, most of your added code is in experiment_server.go, so you tested it the most. Sounds like adding minimal test for other small changes in each unit will be an additional good thing.

backend/src/apiserver/server/experiment_server.go Outdated Show resolved Hide resolved
@chensun chensun changed the title [Backend][Multiuser] Add namespace to Experiment API implementation [Backend][Multi-user] Add namespace to Experiment API implementation Mar 13, 2020
@chensun
Copy link
Member Author

chensun commented Mar 14, 2020

Great work!
/lgtm

Now I realize you emphasized a lot on integration tests for the complete experiment server from end to end. I think that's a good practice, but it's different from existing pattern. It seems with existing pattern, we need some tests for each unit.
/cc @IronPan
for reviewing this part.

I'm okay with your choices because I don't have much context on which would be better and why it is like current convention.

While there're things unit tests cannot really verify - such as the real authorization, I'm with you that unit test is critical and should not be missed -- this is also one of my top complains about our code base. So I added more unit tests following your comments -- thanks again for catching the issue where Default experiment creation would fail in multi-user mode.
Let me know if you found other pieces missing unit tests. Regarding CreateExperiment/GetExperiment/ListExperiments, we have the unit tests in experiment_server_test.go but not in resource_manager_test.go, which I think is probably okay, since experiment_server is a thin wrapper layer, it mostly passes the request to resources_manager. If we have tests in both places, they will likely look almost identical.

@chensun
Copy link
Member Author

chensun commented Mar 16, 2020

/assign @IronPan

// Skip authorization if not multi-user mode.
return nil
}
namespace, err := s.resourceManager.GetNamespaceFromExperimentID(experimentID)
Copy link
Member

Choose a reason for hiding this comment

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

does it make sense to move the GetNamespaceFromExperimentID to inline here?

Copy link
Member Author

Choose a reason for hiding this comment

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

The difference is probably trivial, but I feel like having this wrapper in resource_manager.go is slightly better. It keeps the experiment model details within resource_manager and hides it away from experiment_server.
That said, we might refactor the code to get rid of this canAccessExperiment (just added a TODO), so this is subject to change. So I think we can just keep it as-is for now.

Name: experiment.Name,
Description: experiment.Description,
CreatedAt: &timestamp.Timestamp{Seconds: experiment.CreatedAtInSec},
ResourceReferences: []*api.ResourceReference{
Copy link
Member

Choose a reason for hiding this comment

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

Can we dedup the common code and only add RR field in multi-user mode?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@IronPan
Copy link
Member

IronPan commented Mar 17, 2020

/lgtm

@k8s-ci-robot k8s-ci-robot removed the lgtm label Mar 17, 2020
Copy link
Contributor

@Bobgy Bobgy left a comment

Choose a reason for hiding this comment

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

/lgtm
Looks great @chensun!

@@ -122,7 +133,7 @@ func (s *ExperimentServer) canAccessExperiment(ctx context.Context, experimentID
return util.Wrap(err, "Failed to authorize with the experiment ID.")
}
if len(namespace) == 0 {
return util.NewInternalServerError(errors.New("There is no namespace found"), "There is no namespace found")
return util.NewInternalServerError(errors.New("Empty namespace"), "The experiment doesn't have a valid namespace.")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: not a big issue, would it make sense to allow access to experiment with namespace="" blindly?
because these namespaces are coming from before multi user mode was enabled.

I think you can decide whatever makes sense to you.

Copy link
Member Author

Choose a reason for hiding this comment

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

Initially, I allow listing experiments with empty namespace, but reject accessing experiment with empty namespace. The idea was to allow user see a list, but when they click on it, they get an error message.
Now that we disallowed empty namespace during listing, the experiments with empty namespace is almost invisible. So I'm leaning towards keeping it consistent for both list and get.
Eventually, this shouldn't happen once backfill is done (either via a SQL script or by automated backfill from api-server).

ResourceReferences: resourceReferences,
}}
assert.Equal(t, expectedExperiment, result.Experiments)
tests := []struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Great work! Thanks for improving unit tests!

@chensun
Copy link
Member Author

chensun commented Mar 17, 2020

/retest

1 similar comment
@chensun
Copy link
Member Author

chensun commented Mar 17, 2020

/retest

@IronPan
Copy link
Member

IronPan commented Mar 17, 2020

/lgtm
/approve

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: IronPan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot merged commit 809f97a into kubeflow:master Mar 17, 2020
@chensun chensun deleted the experiment-api-impl branch April 3, 2020 22:34
Jeffwan pushed a commit to Jeffwan/pipelines that referenced this pull request Dec 9, 2020
…ubeflow#3243)

* Experiment API implementation

* Address review comments and add additional unit tests

* Update build file

* fix unit tests

* Reject empty namespace in multi user mode

* Address review comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants