Build Your Own Helm Chart Repository in S3 — With Auto-Generated User Documentation!
Helm chart repositories for everyone. With auto-generated clear instructions on how to add, search, and remove the repository. Right from the repository. Because user documentation matters. Readily available as a Terraform module on GitHub. With a live demo.
Kubernetes and Helm: Release with Ease
Helm charts are great. They allow to deploy even the most complex application into a Kubernetes cluster with a single command and still leave sufficient room for customisation. Once the application is deployed, Helm keeps track of all the assets and makes it easy to upgrade, roll back, and delete releases.
As the Helm website states
Helm helps you manage Kubernetes applications — Helm Charts helps you define, install, and upgrade even the most complex Kubernetes application.
Charts are easy to create, version, share, and publish — so start using Helm and stop the copy-and-paste madness.
Note that the above quote refers to an earlier version of the Helm website. It seems that the “madness” quote has been removed in recent weeks. However, the point about copy-and-paste is still valid.
Writing Your Own Helm Charts
Another great feature of Helm is that writing your own Helm chart is fairly easy; helm
even ships with a command that creates a starter chart.
It does have its quirks and gotchas, as I’ve experienced first hand writing Helm charts for an application with a microservice architecture. However, the overall experience was very positive and I’d use Helm and Helm charts again for orchestrating Kubernetes releases.
Helm also comes with built-in commands to package and index charts so that they can be directly made available in a Helm chart repository; the output is a collection of .tgz
files containing the packaged Helm charts alongside an index.yaml
file containing information about the packaged Helm charts and where to find them. This release process is a natural fit for automation and makes it fairly easy to integrate into a CI/CD pipeline.
Hosting Your Own Helm Chart Repository
With the Helm charts packaged, indexed, and ready to be made available to the world, one question remains: Where do you host the Helm chart repository?
As described in the Helm chart repository guide, a Helm chart repository is in essence a web server capable of serving the repository’s index.yaml
file at its root along with the corresponding packaged artefacts. That’s it.
The official Helm chart repository guide also lists several options for hosting a Helm chart repository, including Google Cloud Storage (note that this is where the stable
and incubator
repositories are hosted), JFrog Artefactory, GitHub pages, and ordinary web servers.
There’s also an interesting article on HackerNoon that describes how to use GitHub as a Helm chart repository. While this introduces the versioning of binary artefacts in GitHub, it also allows to keep the source code as well as all packaged assets under the same GitHub control mechanisms.
User Documentation Matters
However, all of the repositories I could find have the user documentation outside of the repository. So, anyone visiting the repository with a browser gets to see a YAML file. While it contains a plethora of information that’s of great use to helm
, it doesn’t necessarily tell me how to add the repository to my helm
CLI. Most likely that information is documented somewhere else to which there may not be a link from the Helm chart repository.
Sure, it’s easy enough to add a Helm chart repository to your helm
CLI once you’ve done it once or twice but having the user documentation right on the doorstep of the Helm chat repository might make it that bit easier for first time users. For example, how complicated is it to add the Helm chart repository http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com/ to your helm
CLI?!
Sure, you can also add an index.html
file to your Helm chart repository and document the steps to add, search, or remove the Helm chart repository. But this may involve hand crafting the index.html
file which can be time consuming and (if you’re anything like me) error prone.
What if the user documentation in the index.html
file would be auto-generated and uploaded upon creation of the Helm chart repository?
Now, this is what project https://github.com/dumrauf/s3-helm-chart-repo-with-docs was set out to achieve. Helm chart repositories for everyone with clear instructions on how to add, search, and remove the repository. Right from the repository.
Build Your Own Helm Chart Repository
The repository https://github.com/dumrauf/s3-helm-chart-repo-with-docs contains a Terraform module that creates a Helm chart repository using an AWS S3 bucket. The S3 bucket is configured as a static website and contains an auto-generated index.html
file with instructions on using and managing the Helm chart repository.
A live example can be found at http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com.
You Have
Before you can create your own Helm chart repository with auto-generated documentation and start serving Helm charts in a matter of seconds, you need
- an AWS account
- a Terraform CLI
- a log bucket which can be used to store S3 access logs (see also article AWS Logging Buckets for further information or use the Terraform module featured therein to create a log bucket if needed)
Moreover, you probably also have a directory containing some packaged Helm charts along with an index.yaml
file ready to be made available to the world.
You Want
After running the Terraform module in the repository you get an S3 bucket that contains auto-generated documentation and can be used to host a Helm chart repository.
Setup
The input variables for the module are defined in settings/example.tfvars to be
region = "<your-region>"
shared_credentials_file = "/path/to/.aws/credentials"
profile = "<your-profile>"
bucket_name = "<your-bucket-name>"
repository_name = "<your-repository-name>"
log_bucket_id = "<your-log-bucket-id>"
Here, you need to replace the example values with your settings.
Moreover, note that the S3 bucket will be named ${var.repository_name}-${var.bucket_name}
.
Execution
Initialise Terraform by running
terraform init
As a best practice, create a new workspace by running
terraform workspace new example
The Helm chart repository can be planned by running
terraform plan -var-file=settings/example.tfvars
and created by running
terraform apply -var-file=settings/example.tfvars
Terraform Outputs
The module has CLI outputs as well as template files it generates. Here, the template files contain the auto-generated documentation.
CLI Outputs
The module has two outputs, namely helm_chart_repository_bucket_domain_name
and helm_chart_repository_website_endpoint
which are the corresponding Terraform attributes of the newly created Helm chart repository bucket. Point your browser to the output of helm_chart_repository_website_endpoint
by running
echo "Visit http://$(terraform output helm_chart_repository_website_endpoint)"
in order to visit your new Helm chart repository as well as to review the auto-generated documentation.
Generated Template Files
The module also generates a Markdown file as well as an HTML file. Both files are named ${var.repository_name}-${var.bucket_name}.index.{md,html}
. Here, the contents of the HTML file are also uploaded to the S3 bucket as index.html
.
Deletion
The Helm chart repository can be deleted by running
terraform destroy -var-file=settings/example.tfvars
Under the Covers - Quirks and Gotchas
This section goes into the details of how the Terraform module works and which quirks and gotchas I experienced while authoring it.
S3 Bucket with Static Website
The main.tf
file is fairly straightforward and contains aws_s3_bucket
, aws_iam_policy_document
, and aws_s3_bucket_policy
Terraform resources at its core as suggested in the official AWS documentation. Here, the trick was to enable static website hosting but also to get the minimum bucket and ACL permissions right.
Permissions
With a bucket policy giving everyone in the world read access to the bucket, I was initially puzzled about the
acl = "public-read"
suggestion on many blog posts around the web. However, this became clear when reading the official AWS documentation that states
The bucket policy applies only to objects that are owned by the bucket owner. If your bucket contains objects that aren’t owned by the bucket owner, public READ permission on those objects should be granted using the object access control list (ACL).
S3 Server Access Logging
S3 server access logging is enabled by default. However, it should be noted that AWS operates on a best effort server log delivery model.
HTTP(S) Traffic
Note that for the Helm chart repository, the index.html
page is served via the S3 static website endpoint using HTTP while the helm
commands in the instructions refer to the REST API endpoint for the S3 bucket using HTTPS. Furthermore, note that SSL connections are not supported for website endpoints.
Auto-Generating the Documentation
Auto-generating the documentation was the most interesting and fun part of this project. It taught me a lot about rendering templates in Terraform as well as Markdown in browsers.
Where to Render the Documentation
The Terraform module creates the S3 bucket and is therefore the ultimate source of truth when it comes to the website endpoint as well as the corresponding REST API endpoint of the S3 bucket. So, any automated documentation generation would have to get this information from Terraform first. One option to retrieve values from Terraform is to use the
terraform output <named-output>
command. This would allow to import the values into a templating system and generate the documentation.
However, the outputs would have to be defined in Terraform first before they could be used. Moreover, the goal was to provide a one-stop solution that creates a Helm chart repository including documentation. This would mean importing the generated documentation back into Terraform again.
There has to be a better way…
The template_file
Provider
Enter the template_file
provider that does what it says on the tin. Given a template file and some vars
, a template_file
resource renders the file using the vars
provided and makes everything available again in attribute rendered
.
This was exactly what I needed since it allowed me to create the documentation directly in Terraform. But, which language to use for the documentation?
Generating Markdown
Generating HTML can be a tricky business. Markdown provides a simple lightweight markup language that can be converted into many formats, including HTML. This was the right language to generate the user documentation in index.md.tpl
in.
Generating HTML
However, the generated Markdown also needed to be converted to HTML in order to be displayed in the final index.html
file. Here, the idea was to embed the rendered markdown in the HTML file and use a pure in-browser markdown converter to produce the final web page.
This is implemented in template index.html.tpl
using Showdown to render the generated markdown entirely in the visitor’s browser. Moreover, a GitHub like look and feel of the resulting index.html
page was achieved by using a minified GitHub markdown CSS.
The final conversion is eventually done in a few lines of Javascript
<script>
// Inspired by <https://github.com/showdownjs/showdown/wiki/Tutorial:-Markdown-editor-using-Showdown>
document.addEventListener('DOMContentLoaded', function () {
var text = document.getElementById('sourceTA').value,
target = document.getElementById('renderedMarkdown'),
converter = new showdown.Converter(),
html = converter.makeHtml(text);
target.innerHTML = html;
}, false);
</script>
Here, the Javascript extracts the markdown from sourceTA
, converts it, and appends the resulting HTML to renderedMarkdown
.
Saving Auto-Generated Files
For archiving purposes, the markdown and HTML files are both written to disk using the local_file
provider. Here, the file names are prefixed with the corresponding Helm chart repository name in order to avoid collisions.
Uploading the index.html
File
With the documentation generated, all that remained to do was to upload it to the S3 bucket as the shiny new index.html
file using the s3_bucket_object
resource. How Hard Can It Be?!
Well, a tad more that initially expected. When accessing http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com/ via a browser, the page would load, a download would start and then nothing would happen after that. No page was being displayed on screen. That went well then…
This download-instead-of-serving problem is well known and already has its own StackOverflow post. A comment rightfully outlined the root cause of the problem to be a wrong content type as the following curl
command shows
$ curl -I http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com/
HTTP/1.1 200 OK
x-amz-id-2: XniS/NDsD/DqNWrH6Z8lYLuNm6iHkPIaNIG1v0UthfIuESYM564Joe9TZClqsCR4SZznUufU88M=
x-amz-request-id: 6D7A5448108BC4F4
Date: Fri, 28 Dec 2018 22:41:42 GMT
Last-Modified: Fri, 28 Dec 2018 22:41:32 GMT
x-amz-version-id: T1vazfgb2Xrt13R9iUcNTUonPlvZ1fYE
ETag: "f948b8368bf7861c1a0836073d9ae132"
Content-Type: binary/octet-stream
Content-Length: 4045
Server: AmazonS3
Note that the Content-Type: binary/octet-stream
is clearly off and should much rather be Content-Type: text/html
.
The solution did lie in another comment that suggested hard coding the content type to text/html
in the aws_s3_bucket_object
resource used to upload the file to S3, as in
resource "aws_s3_bucket_object" "helm-chart-repository-index-html" {
...
content_type = "text/html"
}
This solved the problem as the following curl
command shows
$ curl -I http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com/
HTTP/1.1 200 OK
x-amz-id-2: /61H1mM6U1Z/6OxBjPjJt1y2Xym1wIiufSmdAWTQHs1BJtiVul+fT5F5CUXBxS2bei/aYMx+0dk=
x-amz-request-id: 8C8CC8D83FC37FFF
Date: Fri, 28 Dec 2018 22:42:48 GMT
Last-Modified: Fri, 28 Dec 2018 22:42:40 GMT
x-amz-version-id: TkEHVJoH95ehr6l6m4ZeBLa6nUikUVlW
ETag: "f948b8368bf7861c1a0836073d9ae132"
Content-Type: text/html
Content-Length: 4045
Server: AmazonS3
Your browser also agrees most likely when pointing it to http://dumrauf-helm-chart-repo.s3-website-us-east-1.amazonaws.com/. Have a go.
Your Experiences?
While this article only describes my experiences, I am highly interested in hearing from you!
Did you come across similar problems? If no, how did you avoid these problems? If yes, how did you resolve them? Feel free to comment on this article or reach out. All feedback is welcome! As always, prove me wrong and I’ll buy you a pint!
Subscribe to How Hard Can It Be?!
Get the latest posts by following us on LinkedIn and Twitter