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

Feature/refresh #273

Merged
merged 19 commits into from
Oct 29, 2024
Merged

Feature/refresh #273

merged 19 commits into from
Oct 29, 2024

Conversation

matthewpeterkort
Copy link
Contributor

@matthewpeterkort matthewpeterkort commented Jul 11, 2024

Link to JIRA ticket if there is one:

New Features

  • Adds the ability to refresh guppy via a POST request to the path _refresh. Guppy must be configured to accept this request via the allowRefresh variable in the guppy config. If this variable is not set, the feature is not available.
  • Authenticated users who present a valid token that has admin_access or * method permissions for service guppy on resource path /guppy_admin are able to make the _refresh request.
  • This request restarts the server, allowing for the ability to update the guppy configurations to reflect any new column additions in the elastic indices as a data submitter.

Breaking Changes

None

Bug Fixes

None

Improvements

None

Dependency updates

None

Deployment changes

  • To enable this feature in helm charts add "allowRefresh": {{ .Values.allowRefresh }} to gen3-helm/helm/guppy/templates/guppy_config.yaml and then in your values.yaml file, specify allowRefresh as the boolean true value.

Object.keys(result).forEach((key) => {
// logic: you have access to a project if you have the following access:
// method 'create' (or '*' - all methods) to service 'guppy' (or '*' - all services)
// on the project resource.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is too broad, and it sort of binds the two roles of data submitter and Guppy admin togather. The restarting of Guppy should be an "admin-level" feature. I think we'd better to have a dedicated "guppy_admin" policy for this feature, which could be service == guppy, method == *, or service == guppy, method == admin_access

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see 713fc1f

@@ -65,6 +65,64 @@ class ArboristClient {
},
);
}

listAuthorizedCreateResources(jwt) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Most of this function is duplicating listAuthorizedResources(jwt), except for the part that check's the user for certain service + methods.
We don't need to duplicate those codes, we should be extracting the part of making request to arborist and checking for user's access in the returned auth mapping into two separated functions, and just reuse them. In this way, we can avoid sending duplicated requests to arborist
See how the fetch for user's auth mapping and check for user's access can be separated in another service
https://github.com/uc-cdis/data-portal/blob/master/src/actions.js#L513
https://github.com/uc-cdis/data-portal/blob/master/src/authMappingUtils.js#L53

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok getAuthMapping function has been created and used twice, once for the reader permissions checking, and again for the guppy admin permissions checking. See 713fc1f

throw new CodedError(data.error.code, data.error.message);
}
const resources = data.resources ? _.uniq(data.resources) : [];
return resources;
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need to execute all those expensive array manipulations for this feature, if your goal is to determine whether am user is Guppy admin or not. This function can simply returns a boolean based on the return value from listAuthorizedCreateResources()
Of couse variable and function names should be updated to relflect this change

Copy link
Contributor Author

@matthewpeterkort matthewpeterkort Jul 26, 2024

Choose a reason for hiding this comment

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

Ok boolean is returned now when checking for refresh permissions

}
await server.stop()
await initializeAndStartServer();
} else if (config.allowRefresh === false) {
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 this check should happens first, before checking for user's token

throw noPermsUser;
}
await server.stop()
await initializeAndStartServer();
Copy link
Contributor

Choose a reason for hiding this comment

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

This solution is generally ok, but it only works if there is only 1 guppy pod/container/server in the system.
In the cases of how Gen3 is usually deployed, which is on a k8s cluster with a load balancer / reverse proxy in front of pods, and also if there are replicas of guppy pods. This solution will only triggers 1 guppy pod to restart each time, which will causes a discrepancy in the states of pods

Copy link

Choose a reason for hiding this comment

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

@mfshao thanks for all the feedback. In the case of replica guppy pods, is there a link you could provide for how requests from the clients are distributed to the guppy replicas? In other words, is there a nginx or helm configuration that fans out the requests?

Copy link

@bwalsh bwalsh Jul 30, 2024

Choose a reason for hiding this comment

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

@mfshao Alternatively, as a brainstorm: guppy could checking mappings on a configurable interval and initiate a restart if the version changed. For example, for index gen3.aced.io_file_0

$ curl  localhost:9200/gen3.aced.io_file_0  | jq '.["gen3.aced.io_file_0"].settings.index.version.created '
>>> "7170399"

On startup, guppy could cache this value for each index, and then monitor the index for changes

Copy link
Contributor

@mfshao mfshao left a comment

Choose a reason for hiding this comment

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

This branch need a rebase with the current master

],
};
} else {
data = await arboristClient.listAuthMapping(jwt);
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 canRefresh() will only be called within initialize(), you don't have to make another call to arborist to get user's ath mapping again because you have already done it in getAccessibleResourcesFromArboristasync()
consider to put the auth mapping in a vairable and use it here instead of making another call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been implemented in recent commits

const resources = data.resources ? _.uniq(data.resources) : [];
return resources;
};

export const canRefresh = async (jwt) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
export const canRefresh = async (jwt) => {
export const checkIfUserCanRefreshServer = async (jwt) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

canRefresh => checkIfUserCanRefreshServer has been implemented

const disabledRefresh = new CodedError(404, '[Refresh] guppy _refresh functionality is not enabled');
throw disabledRefresh;
}
else if (config.allowRefresh) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be just an else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

implemented

dependabot bot and others added 13 commits October 24, 2024 10:31
* chore(deps): bump braces from 3.0.2 to 3.0.3

Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](micromatch/braces@3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* trigger gh action

* trigger travis

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mingfei Shao <mshao1@uchicago.edu>
Bumps [ws](https://github.com/websockets/ws) from 6.2.2 to 6.2.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](websockets/ws@6.2.2...6.2.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mingfei Shao <2475897+mfshao@users.noreply.github.com>
* fix apply hide number resolver to as text agg results

* update dockerfile
Param order was wrong on changed line, put err first.
Refer to: https://expressjs.com/en/guide/error-handling.html
…dis#280)

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-SEND-7926862

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
…e to use newer dependencies (uc-cdis#285)

* PPS-1564 PPS-1567 PPS-1585: update body-parser and package lock file to use newer dependencies
* PPS-1564 PPS-1567 PPS-1585: add file so nvm can set node version
Copy link
Contributor

@mfshao mfshao left a comment

Choose a reason for hiding this comment

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

we are getting close to it, still a few small issues to fix, and we will run our CI over this to make sure there is no regression
I think after fixing these issues and passing CI we could approve

}
},
);
// eslint-disable-next-line no-unused-vars
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need this for the next line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

});
};
// download endpoint for fetching data directly from es
app.post('/download', downloadRouter, (err, req, res, next) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

put // eslint-disable-next-line no-unused-vars above this to satisfy the linter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

};
// download endpoint for fetching data directly from es
app.post('/download', downloadRouter, (err, req, res, next) => {
// eslint-disable-line no-unused-vars
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

}
});

app.post('/_refresh', refreshRouter, (err, req, res, next) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

put // eslint-disable-next-line no-unused-vars above this to satisfy the linter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

const app = express();
app.use(cors());
app.use(helmet());
app.use(bodyParser.json({ limit: '50mb' }));

const refreshRouter = async (req, res, next) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

refreshRouter should be placed after initializeAndStartServer

Copy link
Contributor Author

@matthewpeterkort matthewpeterkort Oct 25, 2024

Choose a reason for hiding this comment

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

fixed

}

const config = {
allowRefresh: inputConfig.allowRefresh || false,
Copy link
Contributor

Choose a reason for hiding this comment

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

allowRefresh is duplicated in this config

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


export const checkIfUserCanRefreshServer = async (data) => {
if (config.internalLocalTest) {
data = {
Copy link
Contributor

Choose a reason for hiding this comment

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

consider fix the failed es-lint check here

Copy link
Contributor Author

@matthewpeterkort matthewpeterkort Oct 25, 2024

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

@mfshao mfshao left a comment

Choose a reason for hiding this comment

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

CI is passing in this PR #292
code-wsie lgtm

@mfshao mfshao merged commit c89aff7 into uc-cdis:master Oct 29, 2024
2 of 4 checks passed
@paulineribeyre
Copy link
Contributor

Authenticated users who present a valid token that has create method permissions on at least one resource path are able to make the _refresh request.

This statement in the PR description and release notes is not accurate

@mfshao
Copy link
Contributor

mfshao commented Nov 4, 2024

Authenticated users who present a valid token that has create method permissions on at least one resource path are able to make the _refresh request.

This statement in the PR description and release notes is not accurate

yeah you are right, I can update the PR description, and release note

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.

9 participants