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

Helm S3 Support #2558

Open
theeltahir opened this issue Oct 24, 2019 · 19 comments
Open

Helm S3 Support #2558

theeltahir opened this issue Oct 24, 2019 · 19 comments
Labels
component:config-management Tools specific issues (helm, kustomize etc) enhancement New feature or request help wanted Extra attention is needed type:usability Enhancement of an existing feature

Comments

@theeltahir
Copy link
Contributor

Summary

Repo server should leverage the Helm CLI for some of the calls to enable better Helm plugin support

Motivation

Currently our team uses S3 to store Helm charts. We are excited to use the 1st class Helm support features slated to arrive in Argo CD 1.3. After building a custom image with the Helm S3 plugin, adding an S3 bucket as a Helm repo results in the following errors

time="2019-10-24T15:52:24Z" level=error msg="finished unary call with code Unknown" error="Get s3://<s3-bucket-name>/charts/index.yaml: unsupported protocol scheme \"s3\"" grpc.code=Unknown grpc.method=GetHelmCharts grpc.request.deadline="2019-10-24T15:53:24Z" grpc.service=repository.RepoServerService grpc.start_time="2019-10-24T15:52:24Z" grpc.time_ms=0.084 span.kind=server system=grpc

After digging in the code further, looks like the GetIndex method acquires the chart index via http calls and not the Helm CLI:

argo-cd/util/helm/client.go

Lines 153 to 201 in 5706a17

func (c *nativeHelmChart) GetIndex() (*Index, error) {
cachedIndex, found := indexCache.Get(c.repoURL)
if found {
log.WithFields(log.Fields{"url": c.repoURL}).Debug("Index cache hit")
i := cachedIndex.(Index)
return &i, nil
}
start := time.Now()
repoURL, err := url.Parse(c.repoURL)
if err != nil {
return nil, err
}
repoURL.Path = path.Join(repoURL.Path, "index.yaml")
req, err := http.NewRequest("GET", repoURL.String(), nil)
if err != nil {
return nil, err
}
if c.creds.Username != "" || c.creds.Password != "" {
// only basic supported
req.SetBasicAuth(c.creds.Username, c.creds.Password)
}
tlsConf, err := newTLSConfig(c.creds)
if err != nil {
return nil, err
}
tr := &http.Transport{TLSClientConfig: tlsConf}
client := http.Client{Transport: tr}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != 200 {
return nil, errors.New("failed to get Index: " + resp.Status)
}
index := &Index{}
err = yaml.NewDecoder(resp.Body).Decode(index)
log.WithFields(log.Fields{"seconds": time.Since(start).Seconds()}).Info("took to get Index")
indexCache.Set(c.repoURL, *index, cache.DefaultExpiration)
return index, err
}

This would lead me to believe that our image with the Helm S3 plugin is ineffective as the http client does not understand the S3 calls.

Proposal

I have not spent enough time with the code base to know if it is feasible but if we leveraged the Helm CLI for some of these calls it might allow the Helm S3 to function properly

@theeltahir theeltahir added the enhancement New feature or request label Oct 24, 2019
@jannfis
Copy link
Member

jannfis commented Oct 24, 2019

Hm, personally I like the idea that ArgoCD could access Helm charts or repositories residing in S3 buckets, but I feel using helm CLI for fetching data from the repository might not be the right way to do it, for various reasons, i.e. you would have to build a custom image (as you did), containing your AWS credentials and additional Helm plugins. Also, ArgoCD certificate & credential management might not work with Helm CLI and custom plugins.

Maybe native S3 support for accessing repositories hosted on S3 in ArgoCD could work, i.e. by leveraging the Minio S3 client (https://github.com/minio/minio-go). This way, we could manage the credentials just like every other repository credentials (i.e. access key as username, secret key as password) and also use custom TLS certificates, in case someone hosts on private Minio instances exposing the S3 API.

What do you think about this idea?

@theeltahir
Copy link
Contributor Author

theeltahir commented Oct 24, 2019

Yeah i was not sure how much of a demand is for this feature, that is why I tried to go the S3 plugin route which we use in our current CI process (and Helmsman prior to Argo migration).

If the team is able to add S3 protocol support via minio, I would be super happy as it would reduce the need for us to build a custom image for this feature.

Edit: I would prefer that we also allow the possibility of using IAM roles for accessing the S3 repo (the argo repo server would be granted read-only access), it would make it less stressful for us for key rotation.

@jannfis
Copy link
Member

jannfis commented Oct 24, 2019

Hm, I've just had a look at our Helm integration code, and it seems we use golang's http.Client for fetching the index.yaml from the chart repository, and then helm fetch ... for getting the actual chart. So it might actually work (as in, a workaround) for you that you put up the index.yaml on a web server, and the actual charts on S3.

On a second thought, maybe using Chart Museum would be another viable choice, since it supports many backends for storing the charts already.

Finally, I can understand the desire for using IAM roles for authentication & authorization, but I think managing those is a little out of scope for ArgoCD, since they're very vendor-specific. But that's just my opinion, there might be others.

@alexec You know the Helm stuff way better than me, so I'd love to hear your thoughts on this topic.

@theeltahir
Copy link
Contributor Author

That is an interesting thought, I'll play around with the permissions on the index.yaml on the bucket to see if that is the only hurdle. That would mean that the S3 plug-in is still needed for the helm fetch operation.

Perhaps Chart Museum is a better product, however that would require us to retool around it. The attractive piece about using S3 is we no longer need to manage the basic auth credentials, and relieve the stress on the key rotation and secrets management.

I am not advocating for the Argo CD to have special provisions for IAM roles (any more than it has now), we would leverage the IAM roles granted to the pods via Kube2IAM/KAIM/IRSA.

@theeltahir
Copy link
Contributor Author

So after updating the permissions on the bucket, looks like using the https URL instead of the S3 URL works, provided the Helm S3 Plugin is installed in our custom image.
https://<chart-repo-bucket>.s3.amazonaws.com/charts/

after adding the chart repo i was able to pull down an existing chart and the UI displays all the values.yaml fields appropriately. I have not tried deploying a release yet but it looks like this is a potential work around.

@jannfis
Copy link
Member

jannfis commented Oct 25, 2019

So after updating the permissions on the bucket, looks like using the https URL instead of the S3 URL works, provided the Helm S3 Plugin is installed in our custom image.
https://<chart-repo-bucket>.s3.amazonaws.com/charts/

after adding the chart repo i was able to pull down an existing chart and the UI displays all the values.yaml fields appropriately. I have not tried deploying a release yet but it looks like this is a potential work around.

Having a workaround is some good news, I hope the installation of charts works that way as well.

I was thinking a little more about native S3 support meanwhile, and I guess it becomes quite difficult when dealing with fetching dependencies, although I have not looked at the code yet how (or if) we resolve them currently. I'm not an avid user of Helm, so I'm unsure whether helm fetch actually resolves and fetches dependencies. If it just fetches the chart's tarball, my naive assumption would be that we could replace it by a custom fetch method as well. But then we'd have to interpret the index.yaml of the Helm repository as well to get the right URL for the chart to fetch. I don't know for if that's what we want, so I'll wait on @alexec 's judgement for that approach.

As a further workaround, instead of using a custom built argocd image (which may or may not be something you are happy to do), you could also opt for an init container that performs a helm plugin install ... for the s3 plugin, possibly from a private location containing a mirror of the plugin in case your ArgoCD pods do not have access to the Internet (or for other reasons you do not want to modify your pod from Internet resources).

@alexec
Copy link
Contributor

alexec commented Oct 25, 2019

This is correct. Our code does not currently support S3.

Argo CD is un-opinionated on what cloud provider you use, and import an S3 library would break that principle.

Our first preference would be to recommend people use HTTP rather than a custom protocol. Have you read this?

https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro

@theeltahir
Copy link
Contributor Author

theeltahir commented Oct 28, 2019

Yeah I figured that would be the case. It looks like the work around works for us right now, register the S3 bucket via the HTTPS URL and ensure that Helm S3 is present in the Argo CD repo server.

I tried using the init container, ultimately it turned out to be cleaner to build the images locally. Main reason turned out that i would need to build a custom image that contains the plugin, just seemed cleaner to build it directly into the image.

FROM argoproj/argocd:v1.3.0-rc2

USER root
RUN apt-get update && \
    apt-get install -y \
        curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
USER argocd
RUN mkdir -p $(helm home)/plugins
ENV HELM_PLUGIN="/home/argocd/.helm/plugins"
ENV AWS_REGION=us-east-1
ARG HELM_S3_VERSION="0.8.0"
ARG S3_PLUGIN_REPO="https://github.com/hypnoglow/helm-s3.git"
RUN helm plugin install ${S3_PLUGIN_REPO} --version ${HELM_S3_VERSION}

@alexec
Copy link
Contributor

alexec commented Oct 29, 2019

See #1105 .

@alexec
Copy link
Contributor

alexec commented Oct 29, 2019

@theelthair it would be amazing if you could document this for other users, just edit this page:

https://github.com/argoproj/argo-cd/edit/master/docs/user-guide/helm.md

@alexec alexec added the help wanted Extra attention is needed label Oct 29, 2019
@theeltahir
Copy link
Contributor Author

Of course, I have some bandwidth coming up that I can contribute an example.

@alexmt alexmt added the good first issue Good for newcomers label Jan 27, 2020
@tomjohnburton
Copy link

@theeltahir do you have an updated way of doing this with helm3?

@jannfis jannfis added component:config-management Tools specific issues (helm, kustomize etc) type:usability Enhancement of an existing feature labels May 14, 2020
pnowy added a commit to pnowy/argo-cd that referenced this issue Jun 13, 2020
argoproj#2558 Based on discussions from:

argoproj#2558
argoproj#2789
argoproj#1105
argoproj#1153

I've spent a lot of time to put everything together to get integration with GCS working. I think it's worth to have documentation for it.
@jansony1
Copy link

Yeah I figured that would be the case. It looks like the work around works for us right now, register the S3 bucket via the HTTPS URL and ensure that Helm S3 is present in the Argo CD repo server.

I tried using the init container, ultimately it turned out to be cleaner to build the images locally. Main reason turned out that i would need to build a custom image that contains the plugin, just seemed cleaner to build it directly into the image.

FROM argoproj/argocd:v1.3.0-rc2

USER root
RUN apt-get update && \
    apt-get install -y \
        curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
USER argocd
RUN mkdir -p $(helm home)/plugins
ENV HELM_PLUGIN="/home/argocd/.helm/plugins"
ENV AWS_REGION=us-east-1
ARG HELM_S3_VERSION="0.8.0"
ARG S3_PLUGIN_REPO="https://github.com/hypnoglow/helm-s3.git"
RUN helm plugin install ${S3_PLUGIN_REPO} --version ${HELM_S3_VERSION}

Hi, after build this customized image, argocd still could not acess s3 hosted helm, with 403 forbidden. Do you how it access iam credentials? I have exec into argo-repo container which do have a iam service account attached with amazon s3 full access policy:

$ aws sts get-caller-identity { "UserId": "AROAT5QZODQXI2HWQPMCV:botocore-session-1594812324", "Account": "269562551342", "Arn": "arn:aws:sts::269562551342:assumed-role/eksctl-test-eks-addon-iamserviceaccount-argo-Role1-1S6RXJRRIXTS7/botocore-session-1594812324" }

Any idea?

@jpke
Copy link

jpke commented Jul 21, 2020

Yeah I figured that would be the case. It looks like the work around works for us right now, register the S3 bucket via the HTTPS URL and ensure that Helm S3 is present in the Argo CD repo server.
I tried using the init container, ultimately it turned out to be cleaner to build the images locally. Main reason turned out that i would need to build a custom image that contains the plugin, just seemed cleaner to build it directly into the image.

FROM argoproj/argocd:v1.3.0-rc2

USER root
RUN apt-get update && \
    apt-get install -y \
        curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
USER argocd
RUN mkdir -p $(helm home)/plugins
ENV HELM_PLUGIN="/home/argocd/.helm/plugins"
ENV AWS_REGION=us-east-1
ARG HELM_S3_VERSION="0.8.0"
ARG S3_PLUGIN_REPO="https://github.com/hypnoglow/helm-s3.git"
RUN helm plugin install ${S3_PLUGIN_REPO} --version ${HELM_S3_VERSION}

Hi, after build this customized image, argocd still could not acess s3 hosted helm, with 403 forbidden. Do you how it access iam credentials? I have exec into argo-repo container which do have a iam service account attached with amazon s3 full access policy:

$ aws sts get-caller-identity { "UserId": "AROAT5QZODQXI2HWQPMCV:botocore-session-1594812324", "Account": "269562551342", "Arn": "arn:aws:sts::269562551342:assumed-role/eksctl-test-eks-addon-iamserviceaccount-argo-Role1-1S6RXJRRIXTS7/botocore-session-1594812324" }

Any idea?

a few things to look for:
-does your k8s node running argo have an iam policy attached to it's role allowing access to the helm s3 repo bucket? (Possibly relevant - migrating from service-role/AmazonEC2RoleforSSM to AmazonSSMManagedInstanceCore requires adding another policy granting s3 access.)
-is static site hosting enabled for the bucket?
-if the bucket is not publicly accessible, is there a bucket policy in place to whitelist the ip of the node (or the nat gateway if running in a vpc?)

@victorboissiere
Copy link
Contributor

Its not working without making index.html public which is not an option for us.
We will have to work with a custom plugin integration

@victorboissiere
Copy link
Contributor

victorboissiere commented Jul 23, 2020

I've added a proxy (not great) bit at least it worked!

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: s3-proxy
    app.kubernetes.io/name: argocd-s3-proxy
    app.kubernetes.io/part-of: argocd
  namespace: argocd
  name: argocd-s3-proxy
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-s3-proxy
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-s3-proxy
    spec:
      containers:
      - name: s3-proxy
        image: pottava/s3-proxy:2.0
        env:
          - name: AWS_ACCESS_KEY_ID
            valueFrom:
              secretKeyRef:
                name: argocd-aws-user
                key: AWS_ACCESS_KEY_ID
          - name: AWS_SECRET_ACCESS_KEY
            valueFrom:
              secretKeyRef:
                name: argocd-aws-user
                key: AWS_SECRET_ACCESS_KEY
          - name: AWS_REGION
            value: eu-west-3
          - name: AWS_S3_BUCKET
            value: qonto-helm-charts
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: 50Mi
            cpu: 10m
          limits:
            memory: 100Mi
            cpu: 50m
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: s3-proxy
    app.kubernetes.io/name: argocd-s3-proxy
    app.kubernetes.io/part-of: argocd
  name: argocd-s3-proxy
  namespace: argocd
spec:
  ports:
  - name: server
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app.kubernetes.io/name: argocd-s3-proxy

Now in the UI, I can add the repository with http://argocd-s3-proxy. However, I still get unsupported protocol s3:// while ArgoCD runs helm2 dependency build (because the index.yaml still point to S3).
I dont understand why, because when I sh manually into the container and cd to the tmp directory it works!

argocd@argocd-repo-server-5c5ff5c684-wmlsr$ cd /tmp/[TEMPORARY_GIT_PROJECT
argocd@argocd-repo-server-5c5ff5c684-wmlsr:/tmp/https:XXXX/applications/pdf$ helm init --client-only
argocd@argocd-repo-server-5c5ff5c684-wmlsr:/tmp/https:XXXX/applications/pdf$ helm2 dependency build
Hang tight while we grab the latest from your chart repositories...
...Unable to get an update from the "local" chart repository (http://127.0.0.1:8879/charts):
	Get http://127.0.0.1:8879/charts/index.yaml: dial tcp 127.0.0.1:8879: connect: connection refused
...Successfully got an update from the "stable" chart repository
Update Complete.
Saving 1 charts
Downloading XXX from repo http://XXXX
Deleting outdated charts

EDIT:

Using Helm3 fixed the issue all together. The S3 proxy still apply.

@artem-kosenko
Copy link

yep. chartmuseum in front of S3 works perfect and resolve all issue related private helm repository configuration.
Thanks for comments! Sometime workarounds might be pretty well 👍

@ognjen-it
Copy link

ognjen-it commented Jan 17, 2022

Hi @victorboissiere ,

you wrote that you set somewhere Helm3, but you didn't write where :)
I deployed argocd-s3-proxy as well as you, but still have problems when trying to deploy the app:

Unable to create application: application spec is invalid: InvalidSpecError: Unable to get app details: rpc error: code = Unknown desc = helm pull --destination /tmp/helm111713662 --version 0.0.1 --repo http://argocd-s3-proxy:8080/charts my-app failed exit status 1: Error: scheme "s3" not supported

Could you please write how did you resolve the problem with little more detail?

EDIT:
I think that the problem is with index.yaml and the push command.
If you push your chart with next command:
helm s3 push my-app-0.0.1.tgz ognjen-repo
index.yaml will have URL: s3://my-bucket-name/charts/my-app-0.0.1.tgz

However, if you push with a relative path like the next command:
helm s3 push --relative ./my-app-0.0.1.tgz ognjen-repo
index.yaml will have URL: my-app-0.0.1.tgz

then, you will be able to create an application in ArgoCD.

However, if you push chart with relative path, you can't delete old chart with helm s3 delete ..

@mballoni
Copy link

Hey folks! Any plan on this one? Would be super to have this support, simplify a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:config-management Tools specific issues (helm, kustomize etc) enhancement New feature or request help wanted Extra attention is needed type:usability Enhancement of an existing feature
Projects
None yet
Development

No branches or pull requests