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: helm requirements.yaml support #3805

Merged
merged 41 commits into from Oct 14, 2019

Conversation

nchashch
Copy link
Contributor

@nchashch nchashch commented May 24, 2019

Todo

  1. Implement extractPackageFile
  2. Implement updateDependencies
  3. Add tests
  4. Add datasource for helm chart repositories
  5. Test with actual charts (version without caching)
  6. Add caching for helm datasource
  7. Get test coverage to 100%
  8. Test with actual charts (version with caching)

This PR adds helm manager.

Closes #3414

* Change fileMatch in definitions.js from Chart.ya?ml to requirements.ya?ml
@nchashch nchashch changed the title feat: Add helm (kubernetes package manager) support feat: Add helm (kubernetes package manager) support (WIP) May 24, 2019
@nchashch nchashch changed the title feat: Add helm (kubernetes package manager) support (WIP) feat: Add helm (kubernetes package manager) support Jun 4, 2019
@rarkins
Copy link
Collaborator

rarkins commented Jun 11, 2019

@nchashch thanks for this contribution. Can you educate me a little on how this works? I had assumed that Helm charts would just reference Docker images, so was surprised to see a new datasource. When I look at the extract function, it appears to reference only other charts. Can you describe to me how it all works?

@nchashch
Copy link
Contributor Author

@rarkins There is a requrements.yaml file, that lists all dependencies (other charts) specifying dependency name, version, and repository url (see reference 1). A repository specified by the url is an HTTP server that provides an index.yaml file which lists all available packages and their versions (see reference 2).

Helm datasource downloads and caches the index.yaml file (it is around 5 MB for the official repository: https://kubernetes-charts.storage.googleapis.com/index.yaml), and then extracts a list of versions for the lookupName package. Then the manager just extracts and updates the values in requirements.yaml as usual.

From the docs I have read it looks like an image (or images) can be specified with a docker container name and tag in values.yaml file like here, and then used for lookup in another file (see reference 3). There might be other ways to specify docker images, like specifying them directly in Chart.yaml, but I am not sure.

It might be possible to also update docker images for helm charts (maybe in a separate manager?), but I think it would be pretty difficult, because it looks like values.yaml can have arbitrary structure. So this PR only handles dependencies in requirements.yaml.

@rarkins
Copy link
Collaborator

rarkins commented Jun 12, 2019

Thanks, I understand better now. As you suggest, we may need to do the image renovating for Helm in another PR

stage: 'package',
type: 'object',
default: {
fileMatch: ['(^|/)requirements.ya?ml$'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
fileMatch: ['(^|/)requirements.ya?ml$'],
fileMatch: ['(^|/)requirements\\.ya?ml$'],

getPkgReleases,
};

async function getPkgReleases({ lookupName, helmRepository }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of this custom new helmRepository field, can we reuse our existing registryUrls concept?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There can be multiple repositories in a single requirements.yaml file, and each dependency is associated with it's own repository. So if registryUrls is used, we will have to pass helmRepository with every dependency anyway in order to identify which registry url to use.

Getting index.yaml for every registry in registryUrls and searching for lookupName in them without helmRepository wouldn't work, because packages can be duplicated in repositories, and there is no guarantee that the right one will be found. Also it would be more expensive.

if (!res || !res.body) {
logger.warn(
{ dependency: lookupName },
`Received invalid index.yaml from ${helmRepository}`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Technically we haven't tried parsing the yaml yet so this message might confuse people.

}
}
let doc;
if (!cachedIndex) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the second if (!cachedIndex) check in this block, they should probably be combined

deps = doc.dependencies.map(dep => ({
depName: dep.name,
currentValue: dep.version,
helmRepository: dep.repository,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Switch to registryUrls here

depName: dep.name,
currentValue: dep.version,
helmRepository: dep.repository,
datasource: 'helm',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there any possibility of non-helm datasources here? If not then datasource: 'helm' could be pushed one level up into the response, rather than be inside each dep.

@rarkins
Copy link
Collaborator

rarkins commented Jun 12, 2019

@nchashch could you also make sure that the data we are caching is the most efficient/optimal dataset? e.g. if the file is 5MB raw, then it's probably even more when parsed, but we maybe only need 200kb of it and could cache only that.

@rarkins
Copy link
Collaborator

rarkins commented Jun 12, 2019

I'm still trying to consider if we should have one package manager (helm) or two eventually (helm-charts and helm-images).

Also, I'm wondering if instead of looking for requirements.yaml - for which there could be a lot of false hits, we instead look for directories with Chart.yaml and if found then we will look for requirements.yaml and values.yaml next to it to confirm. This will make it easier to add the values.yaml support in future.

What do you think?

@nchashch
Copy link
Contributor Author

@rarkins This PR is ready for review. Now only versions are cached by the datasource, instead of the whole parsed index.yaml.

@rarkins
Copy link
Collaborator

rarkins commented Jun 18, 2019

@nchashch can you suggest some demo repos I can fork and test against?

@ig0rsky
Copy link

ig0rsky commented Sep 16, 2019

@pharindoko @rarkins any updates?

@pharindoko
Copy link

Sorry I do not have the time to test it.

@JamieMagee
Copy link
Contributor

I'm testing this against the helm/charts repository and I'm encountering some errors. I'm getting a lot of unsupported version messages, for values that should be supported semver versions

DEBUG: Dependency elasticsearch has unsupported value ^1.10.0 (repository=helm/charts)
DEBUG: Dependency kibana has unsupported value ^0.16.0 (repository=helm/charts)
DEBUG: Dependency filebeat has unsupported value ^1.0.0 (repository=helm/charts)
DEBUG: Dependency logstash has unsupported value ^1.1.0 (repository=helm/charts)
DEBUG: Dependency fluentd has unsupported value ^1.0.0 (repository=helm/charts)
DEBUG: Dependency fluent-bit has unsupported value ^0.13.0 (repository=helm/charts)
DEBUG: Dependency fluentd-elasticsearch has unsupported value ^1.0.0 (repository=helm/charts)
DEBUG: Dependency nginx-ldapauth-proxy has unsupported value ^0.1.0 (repository=helm/charts)

@@ -0,0 +1,123 @@
import { extractPackageFile } from '../../../lib/manager/helm-requirements/extract';

const platform: any = global.platform;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you change this to

import { platform as _platform } from '../../../lib/platform';

const platform: any = _platform;

@JamieMagee
Copy link
Contributor

@nchashch If you're interested in getting this merged, there should be just a few changes required:

  1. Merge in changes from latest master
  2. Make changes around platform no longer being global

You can see how it works on my fork of the helm/charts repo here.

As for the unsupported version messages I mentioned above, I realise now that your changes don't have support for version ranges. We open an issue for that, and add it later.

* platform is no longer global, and has to be imported
* Import platform in extract.spec.js
@nchashch
Copy link
Contributor Author

nchashch commented Oct 1, 2019

@JamieMagee I have merged master, and added platform imports instead of using it as a global.

@JamieMagee
Copy link
Contributor

@nchashch Thanks for the update.

I'll review this a little more thoroughly tomorrow, but initially this looks fine. We can open another issue to support pinning/version ranges and I can do the TypeScript conversion for lib/datasource/helm/index.js a separately.

@nchashch
Copy link
Contributor Author

nchashch commented Oct 3, 2019

@JamieMagee Thanks!

}
if (
gotErr.statusCode === 429 ||
(gotErr.statusCode > 500 && gotErr.statusCode < 600)
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
(gotErr.statusCode > 500 && gotErr.statusCode < 600)
(gotErr.statusCode >= 500 && gotErr.statusCode < 600)

});
const cacheMinutes = 20;
await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
} catch (yamlErr) {
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
} catch (yamlErr) {
} catch (err) {

Also add a logger.warn here with text about loading YAML, and logger.debug with the full error

logger.warn(`Received invalid response from ${repository}`);
return null;
}
} catch (gotErr) {
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
} catch (gotErr) {
} catch (err) {

const searchString = `${oldVersion}`;
const newString = `${newValue}`;
let newFileContent = fileContent;
// Search starts at 'dependencies' instaed of `${depName}` because fields in a YAML record
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
// Search starts at 'dependencies' instaed of `${depName}` because fields in a YAML record
// Search starts at 'dependencies' instead of `${depName}` because fields in a YAML record

searchString,
newString
);
// Compare the parsed toml structure of old and new
Copy link
Contributor

Choose a reason for hiding this comment

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

TOML or YAML?

JamieMagee
JamieMagee previously approved these changes Oct 4, 2019
* Treat 500 response as server error in getRepositoryData in helm
  datasource
@rarkins rarkins changed the title feat: Add helm (kubernetes package manager) support feat: helm requirements.yaml support Oct 14, 2019
@rarkins rarkins merged commit 0fffbae into renovatebot:master Oct 14, 2019
@renovate-bot
Copy link
Collaborator

🎉 This PR is included in version 19.61.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Renovating helm charts
6 participants