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

feat(publish): upload apps to App Hub #532

Merged
merged 14 commits into from
May 28, 2021
Merged

feat(publish): upload apps to App Hub #532

merged 14 commits into from
May 28, 2021

Conversation

Birkbjo
Copy link
Member

@Birkbjo Birkbjo commented Mar 11, 2021

This adds a command that is pretty similar to deploy. It extracts some information from the built appBundle, however this uploads that bundle to App Hub instead of a DHIS2-instance. There's some code that is duplicated from publish (eg. dumpHttpError), could probably extract some of this to a common file. I didn't want to change more code than necessary.

This should mainly be used with app-platform apps, and it's thus very simple to use with a bundled app. However I added an escape hatch so that apps that are not migrated to the platform may use this to upload any arbitrary bundle to the app hub. That's why we have options like appId, file and file-version. There are not intended to be use for overriding the config from a built app, as we do not want discrepancies from the uploaded app-information and the app-manifest.

How to test it?

API key generation is live on both staging and live server.

  • Log in to https://staging.apps.dhis2.org/
  • Navigate Api Key, and generate a new key
  • Use API-key with --apiKey or set as env with D2_apiKey=apiKey when using the CLI.

TODO:

  • Initial implementation
  • Initial docs

@Birkbjo Birkbjo requested a review from amcgee March 11, 2021 16:17
@Birkbjo Birkbjo marked this pull request as draft March 11, 2021 16:58
@Birkbjo Birkbjo changed the title feat(publish): implement upload of version to App Hub feat(publish): upload apps to App Hub Mar 11, 2021
@Birkbjo Birkbjo marked this pull request as ready for review March 17, 2021 15:52
Comment on lines 99 to 161
type: 'input',
name: 'minVersion',
message: 'Minimum DHIS2 version supported',
when: () => !params.minVersion,
validate: v =>
isValidServerVersion(v) ? true : 'Invalid server version',
},
{
type: 'input',
name: 'maxVersion',
message: 'Maximum DHIS2 version supported',
when: () => !params.maxVersion,
validate: v =>
!v || isValidServerVersion(v) ? true : 'Invalid server version',
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe the DHIS min and max versions should be specified in d2.config.js instead of as command parameters 🤔 It would make publishing through CI easier as the publish command and its parameters run by the CI action would not have to change

Copy link
Member

Choose a reason for hiding this comment

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

👍 agreed, I think that would be much cleaner. We can keep this as-is for now and add that later if we want (@Birkbjo would you prefer to do it now or in a separate change?)

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree! That would be great, however I didn't want to make this dependent on adding support for that as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Add an issue to the CLI project in Jira so we don't forget?

@amcgee
Copy link
Member

amcgee commented Apr 9, 2021

@Birkbjo is this blocked by anything? Ready for a final review or not yet?

@amcgee
Copy link
Member

amcgee commented Apr 9, 2021

Thought: maybe we should use the environment variable D2_APP_HUB_API_KEY or something a bit more explicit? D2_APIKEY could be for anything, we might have need of other api keys in the future?

@Birkbjo
Copy link
Member Author

Birkbjo commented Apr 9, 2021

Thought: maybe we should use the environment variable D2_APP_HUB_API_KEY or something a bit more explicit? D2_APIKEY could be for anything, we might have need of other api keys in the future?

This is due to the built in resolution of env-vars to yargs params. Should I move away from using that in this case? Or else you would need to type --app_hub_api_key in params as far as I know.

@amcgee
Copy link
Member

amcgee commented Apr 9, 2021 via email

}

const promptForConfig = async params => {
if (process.env.CI && (!params.apikey || !params.minVersion)) {
Copy link
Member

Choose a reason for hiding this comment

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

@Birkbjo I think this will throw if the API key is passed as an env var in CI, right? We actually should prefer to use env vars in CI (since the api key is secret) so probably this should be:

const apiKey = params.apiKey || process.env.D2_APP_HUB_API_KEY
if (process.env.CI && (!apiKey)) { ... }
// ...
when: () => !apiKey

Also - it looks like the option to specify minDHIS2Version in the config file was added, but this check happens before the config is parsed, is that incorrect?

Copy link
Member Author

@Birkbjo Birkbjo May 10, 2021

Choose a reason for hiding this comment

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

@Birkbjo I think this will throw if the API key is passed as an env var in CI, right? We actually should prefer to use env vars in CI

Agree that we should prefer env-vars. But good catch, this would work with the previous env-var, as that would be resolved by yargs - but I totally forgot that this would not work when I changed the name of the env-vars.

Copy link
Member Author

@Birkbjo Birkbjo May 10, 2021

Choose a reason for hiding this comment

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

Also - it looks like the option to specify minDHIS2Version in the config file was added, but this check happens before the config is parsed, is that incorrect?

At the moment it will only prompt for the version if used with the --file-parameter, if resolved from a d2-platform path, it will use the one in the manifest/d2.config. But yes you are correct, and I will remove that check as it's unnecessary.

Copy link
Member

@amcgee amcgee left a comment

Choose a reason for hiding this comment

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

Looking good! I added some suggestions just to clean up the logic a touch

cli/src/commands/publish.js Outdated Show resolved Hide resolved
cli/src/commands/publish.js Outdated Show resolved Hide resolved
cli/src/commands/publish.js Outdated Show resolved Hide resolved

await client.post(uploadAppUrl, formData, {
headers: formData.getHeaders(),
timeout: 30000, // Ensure we have enough time to upload a large zip file
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
timeout: 30000, // Ensure we have enough time to upload a large zip file
timeout: 300000, // Ensure we have enough time to upload a large zip file

For me on a mildy-fast internet connection I was sometimes having timeouts uploading an app, let's make this nice and long (5 minutes)

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 also ensure the app hub backend has a long timeout for that endpoint 🤔

Copy link
Member

Choose a reason for hiding this comment

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

@mediremi agreed, do you know what the timeout currently is? It's at least more than 30 seconds (the previous value) since I was able to upload after bumping the client timeout

Copy link
Member Author

Choose a reason for hiding this comment

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

As far as I can see we have not specified it anywhere, and the docs says its a default value of 10 seconds, but it's obviously not? Maybe it's different for streamed payloads.

Copy link
Contributor

Choose a reason for hiding this comment

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

The timeout on our load balancer in AWS is 60 seconds.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since we support bundles of up to 20MB maybe we should try to increase that to ~5mins?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me. The docs on how to do so is here: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-idle-timeout.html

We can do it using the AWS GUI as long as we don't destroy/recreate our environments that often.

We should document the AWS setup at some point as well so we know what customization we apply.

Copy link
Member Author

@Birkbjo Birkbjo May 27, 2021

Choose a reason for hiding this comment

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

@varl that is different than timeout right? The timeout is for how long a request is allowed to take when uploading something. AWS describes that timeout as idle: " If no data has been sent or received by the time that the idle timeout period elapses, the load balancer closes the connection." Data is still being sent (hopefully) when uploading takes longer than that, so to me that doesn't seem to be the same thing as we're talking about here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure, you may be correct.

For each request that a client makes through a Classic Load Balancer, the load balancer maintains two connections. The front-end connection is between the client and the load balancer. The back-end connection is between the load balancer and a registered EC2 instance. The load balancer has a configured idle timeout period that applies to its connections.

This also stood out to me as relevant:

To ensure that lengthy operations such as file uploads have time to complete, send at least 1 byte of data before each idle timeout period elapses, and increase the length of the idle timeout period as needed.

The way I interpret the docs is that if a client uploads a file, it is sent to the load balancer, and when it's complete the load balancer sends it to the backend. It implies that the data isn't just streamed through one connection to another.

If that's true, then the docs makes sense to me since if it takes longer to upload the file to the load balancer than it maintains the connection to the backend, it would close the connection it has to the backend before the file upload completes.

I may be misunderstanding the docs, and I cannot find other docs explaining it clearly. We pay for the Load Balancer on a "per GB transferred" basis too, but I don't know if that means anything. 🤷

@Birkbjo Birkbjo requested a review from amcgee May 21, 2021 11:58
Copy link
Member

@amcgee amcgee left a comment

Choose a reason for hiding this comment

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

Looks good, let's get this in! 🍾

@Birkbjo Birkbjo merged commit b8c86b6 into master May 28, 2021
@Birkbjo Birkbjo deleted the feat/publish-command branch May 28, 2021 10:31
dhis2-bot added a commit that referenced this pull request May 28, 2021
# [6.2.0](v6.1.3...v6.2.0) (2021-05-28)

### Features

* **publish:** upload apps to App Hub ([#532](#532)) ([b8c86b6](b8c86b6))
@dhis2-bot
Copy link
Contributor

🎉 This PR is included in version 6.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

5 participants