1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Merge pull request #475 from renanaAkeyless/main

Add Akeyless provider
This commit is contained in:
Lucas Severo Alves 2021-11-16 13:09:20 +01:00 committed by GitHub
commit 2c07e7d49a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1624 additions and 23 deletions

View file

@ -13,6 +13,7 @@ Multiple people and organizations are joining efforts to create a single Externa
- [AWS Secrets Manager](https://external-secrets.io/provider-aws-secrets-manager/)
- [AWS Parameter Store](https://external-secrets.io/provider-aws-parameter-store/)
- [Akeyless](https://www.akeyless.io/)
- [Hashicorp Vault](https://www.vaultproject.io/)
- [Google Cloud Secrets Manager](https://external-secrets.io/provider-google-secrets-manager/)
- [Azure Key Vault](https://external-secrets.io/provider-azure-key-vault/)
@ -43,6 +44,7 @@ Multiple people and organizations are joining efforts to create a single Externa
| [Gitlab Project Variables](https://external-secrets.io/provider-gitlab-project-variables/) | alpha | [@Jabray5](https://github.com/Jabray5) |
| Alibaba Cloud KMS | alpha | [@ElsaChelala](https://github.com/ElsaChelala) |
| [Oracle Vault]( https://external-secrets.io/provider-oracle-vault) | alpha | [@KianTigger](https://github.com/KianTigger) |
| [Akeyless]( https://external-secrets.io/provider-akeyless) | alpha | [@renanaAkeyless](https://github.com/renanaAkeyless) |
## Documentation

View file

@ -0,0 +1,42 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
)
// AkeylessProvider Configures an store to sync secrets using Akeyless KV.
type AkeylessProvider struct {
// Akeyless GW API Url from which the secrets to be fetched from.
AkeylessGWApiURL *string `json:"akeylessGWApiURL"`
// Auth configures how the operator authenticates with Akeyless.
Auth *AkeylessAuth `json:"authSecretRef"`
}
type AkeylessAuth struct {
SecretRef AkeylessAuthSecretRef `json:"secretRef"`
}
// AkeylessAuthSecretRef
//AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.
type AkeylessAuthSecretRef struct {
// The SecretAccessID is used for authentication
AccessID esmeta.SecretKeySelector `json:"accessID,omitempty"`
AccessType esmeta.SecretKeySelector `json:"accessType,omitempty"`
AccessTypeParam esmeta.SecretKeySelector `json:"accessTypeParam,omitempty"`
}

View file

@ -42,6 +42,10 @@ type SecretStoreProvider struct {
// +optional
AzureKV *AzureKVProvider `json:"azurekv,omitempty"`
// Akeyless configures this store to sync secrets using Akeyless Vault provider
// +optional
Akeyless *AkeylessProvider `json:"akeyless,omitempty"`
// Vault configures this store to sync secrets using Hashi provider
// +optional
Vault *VaultProvider `json:"vault,omitempty"`

View file

@ -102,6 +102,65 @@ func (in *AWSProvider) DeepCopy() *AWSProvider {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AkeylessAuth) DeepCopyInto(out *AkeylessAuth) {
*out = *in
in.SecretRef.DeepCopyInto(&out.SecretRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessAuth.
func (in *AkeylessAuth) DeepCopy() *AkeylessAuth {
if in == nil {
return nil
}
out := new(AkeylessAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AkeylessAuthSecretRef) DeepCopyInto(out *AkeylessAuthSecretRef) {
*out = *in
in.AccessID.DeepCopyInto(&out.AccessID)
in.AccessType.DeepCopyInto(&out.AccessType)
in.AccessTypeParam.DeepCopyInto(&out.AccessTypeParam)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessAuthSecretRef.
func (in *AkeylessAuthSecretRef) DeepCopy() *AkeylessAuthSecretRef {
if in == nil {
return nil
}
out := new(AkeylessAuthSecretRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AkeylessProvider) DeepCopyInto(out *AkeylessProvider) {
*out = *in
if in.AkeylessGWApiURL != nil {
in, out := &in.AkeylessGWApiURL, &out.AkeylessGWApiURL
*out = new(string)
**out = **in
}
if in.Auth != nil {
in, out := &in.Auth, &out.Auth
*out = new(AkeylessAuth)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessProvider.
func (in *AkeylessProvider) DeepCopy() *AkeylessProvider {
if in == nil {
return nil
}
out := new(AkeylessProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlibabaAuth) DeepCopyInto(out *AlibabaAuth) {
*out = *in
@ -794,6 +853,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(AzureKVProvider)
(*in).DeepCopyInto(*out)
}
if in.Akeyless != nil {
in, out := &in.Akeyless, &out.Akeyless
*out = new(AkeylessProvider)
(*in).DeepCopyInto(*out)
}
if in.Vault != nil {
in, out := &in.Vault, &out.Vault
*out = new(VaultProvider)

View file

@ -54,6 +54,94 @@ spec:
maxProperties: 1
minProperties: 1
properties:
akeyless:
description: Akeyless configures this store to sync secrets using
Akeyless Vault provider
properties:
akeylessGWApiURL:
description: Akeyless GW API Url from which the secrets to
be fetched from.
type: string
authSecretRef:
description: Auth configures how the operator authenticates
with Akeyless.
properties:
secretRef:
description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM:
AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.'
properties:
accessID:
description: The SecretAccessID is used for authentication
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
accessType:
description: A reference to a specific 'key' within
a Secret resource, In some instances, `key` is a
required field.
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
accessTypeParam:
description: A reference to a specific 'key' within
a Secret resource, In some instances, `key` is a
required field.
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
type: object
required:
- secretRef
type: object
required:
- akeylessGWApiURL
- authSecretRef
type: object
alibaba:
description: Alibaba configures this store to sync secrets using
Alibaba Cloud provider

View file

@ -54,6 +54,94 @@ spec:
maxProperties: 1
minProperties: 1
properties:
akeyless:
description: Akeyless configures this store to sync secrets using
Akeyless Vault provider
properties:
akeylessGWApiURL:
description: Akeyless GW API Url from which the secrets to
be fetched from.
type: string
authSecretRef:
description: Auth configures how the operator authenticates
with Akeyless.
properties:
secretRef:
description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM:
AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.'
properties:
accessID:
description: The SecretAccessID is used for authentication
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
accessType:
description: A reference to a specific 'key' within
a Secret resource, In some instances, `key` is a
required field.
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
accessTypeParam:
description: A reference to a specific 'key' within
a Secret resource, In some instances, `key` is a
required field.
properties:
key:
description: The key of the entry in the Secret
resource's `data` field to be used. Some instances
of this field may be defaulted, in others it
may be required.
type: string
name:
description: The name of the Secret resource being
referred to.
type: string
namespace:
description: Namespace of the resource being referred
to. Ignored if referent is not cluster-scoped.
cluster-scoped defaults to the namespace of
the referent.
type: string
type: object
type: object
required:
- secretRef
type: object
required:
- akeylessGWApiURL
- authSecretRef
type: object
alibaba:
description: Alibaba configures this store to sync secrets using
Alibaba Cloud provider

68
docs/provider-akeyless.md Normal file
View file

@ -0,0 +1,68 @@
## Akeyless Vault
External Secrets Operator integrates with [Akeyless API](https://docs.akeyless.io/reference#v2).
### Authentication
The API requires an access-id, access-type and access-Type-param.
The supported auth-methods and their params are:
| accessType | accessTypeParam |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api_key` | The access key. |
| `k8s` | The k8s configuration name |
| `aws_iam` | - |
| `gcp` | The gcp audience |
| `azure_ad` | azure object id (optional) |
form more information about [Akeyless Authentication Methods](https://docs.akeyless.io/docs/access-and-authentication-methods)
### Akeless credentials secret
Create a secret containing your credentials:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: akeylss-secret-creds
type: Opaque
stringData:
accessId: "p-XXXX"
accessType: # k8s/aws_iam/gcp/azure_ad/api_key
accessTypeParam: # can be one of the following: k8s-conf-name/gcp-audience/azure-obj-id/access-key
```
### Update secret store
Be sure the `akeyless` provider is listed in the `Kind=SecretStore` and the `akeylessGWApiURL` is set (def: "https://api.akeless.io".
```yaml
{% include 'akeyless-secret-store.yaml' %}
```
### Creating external secret
To get a secret from Akeyless and secret it on the Kubernetes cluster, a `Kind=ExternalSecret` is needed.
```yaml
{% include 'akeyless-external-secret.yaml' %}
```
#### Using DataFrom
DataFrom can be used to get a secret as a JSON string and attempt to parse it.
```yaml
{% include 'akeyless-external-secret-json.yaml' %}
```
### Getting the Kubernetes secret
The operator will fetch the secret and inject it as a `Kind=Secret`.
```
kubectl get secret akeyless-secret-to-create -o jsonpath='{.data.secretKey}' | base64 -d
```
```
kubectl get secret akeyless-secret-to-create-json -o jsonpath='{.data}'
```

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Secret
metadata:
name: akeylss-secret-creds
type: Opaque
stringData:
accessId: "p-XXXX"
accessType: # k8s/aws_iam/gcp/azure_ad/api_key
accessTypeParam: # can be one of the following: k8s-conf-name/gcp-audience/azure-obj-id/access-key

View file

@ -0,0 +1,18 @@
apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
metadata:
name: akeyless-external-secret-example-json
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: akeyless-secret-store # Must match SecretStore on the cluster
target:
name: akeyless-secret-to-create-json # Name for the secret to be created on the cluster
creationPolicy: Owner
# for json formatted secrets: each key in the json will be used as the secret key in the SECRET k8s target object
dataFrom:
- key: secret-name # Full path of the secret on Akeyless

View file

@ -0,0 +1,19 @@
apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
metadata:
name: akeyless-external-secret-example
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: akeyless-secret-store # Must match SecretStore on the cluster
target:
name: akeyless-secret-to-create # Name for the secret to be created on the cluster
creationPolicy: Owner
data:
- secretKey: secretKey # Key given to the secret to be created on the cluster
remoteRef:
key: secret-name # Full path of the secret on Akeyless

View file

@ -0,0 +1,20 @@
apiVersion: external-secrets.io/v1alpha1
kind: SecretStore
metadata:
name: akeyless-secret-store
spec:
provider:
akeyless:
# URL of your akeyless API
akeylessGWApiURL: "https://api.akeyless.io"
authSecretRef:
secretRef:
accessID:
name: akeylss-secret-creds
key: accessId
accessType:
name: akeylss-secret-creds
key: accessType
accessTypeParam:
name: akeylss-secret-creds
key: accessTypeParam

View file

@ -54,6 +54,9 @@ kubectl run --rm \
--env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
--env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-}" \
--env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET:-}" \
--env="AKEYLESS_ACCESS_ID=${AKEYLESS_ACCESS_ID:-}" \
--env="AKEYLESS_ACCESS_TYPE=${AKEYLESS_ACCESS_TYPE:-}" \
--env="AKEYLESS_ACCESS_TYPE_PARAM=${AKEYLESS_ACCESS_TYPE_PARAM:-}" \
--env="TENANT_ID=${TENANT_ID:-}" \
--env="VAULT_URL=${VAULT_URL:-}" \
--env="GITLAB_TOKEN=${GITLAB_TOKEN:-}" \

View file

@ -0,0 +1,49 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[akeyless] ", func() {
f := framework.New("eso-akeyless")
accessID := os.Getenv("AKEYLESS_ACCESS_ID")
accessType := os.Getenv("AKEYLESS_ACCESS_TYPE")
accessTypeParam := os.Getenv("AKEYLESS_ACCESS_TYPE_PARAM")
prov := newAkeylessProvider(f, accessID, accessType, accessTypeParam)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),
Entry(common.NestedJSONWithGJSON(f)),
Entry(common.JSONDataFromSync(f)),
Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataWithTemplate(f)),
Entry(common.DockerJSONConfig(f)),
Entry(common.DataPropertyDockerconfigJSON(f)),
Entry(common.SSHKeySync(f)),
Entry(common.SSHKeySyncDataProperty(f)),
Entry(common.SyncWithoutTargetName(f)),
Entry(common.JSONDataWithoutTargetName(f)),
)
})

View file

@ -0,0 +1,227 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
aws_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/aws"
azure_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/azure"
gcp_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/gcp"
"github.com/akeylesslabs/akeyless-go/v2"
//nolint
. "github.com/onsi/ginkgo"
//nolint
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/e2e/framework"
)
type akeylessProvider struct {
accessID string
accessType string
accessTypeParam string
framework *framework.Framework
restAPIClient *akeyless.V2ApiService
}
var apiErr akeyless.GenericOpenAPIError
const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
func newAkeylessProvider(f *framework.Framework, accessID, accessType, accessTypeParam string) *akeylessProvider {
prov := &akeylessProvider{
accessID: accessID,
accessType: accessType,
accessTypeParam: accessTypeParam,
framework: f,
}
restAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
Servers: []akeyless.ServerConfiguration{
{
URL: "https://api.akeyless.io",
},
},
}).V2Api
prov.restAPIClient = restAPIClient
BeforeEach(prov.BeforeEach)
return prov
}
// CreateSecret creates a secret.
func (a *akeylessProvider) CreateSecret(key, val string) {
token, err := a.GetToken()
Expect(err).ToNot(HaveOccurred())
ctx := context.Background()
gsvBody := akeyless.CreateSecret{
Name: key,
Value: val,
Token: &token,
}
_, _, err = a.restAPIClient.CreateSecret(ctx).Body(gsvBody).Execute()
Expect(err).ToNot(HaveOccurred())
}
func (a *akeylessProvider) DeleteSecret(key string) {
token, err := a.GetToken()
Expect(err).ToNot(HaveOccurred())
ctx := context.Background()
gsvBody := akeyless.DeleteItem{
Name: key,
Token: &token,
}
_, _, err = a.restAPIClient.DeleteItem(ctx).Body(gsvBody).Execute()
Expect(err).ToNot(HaveOccurred())
}
func (a *akeylessProvider) BeforeEach() {
// Creating an Akeyless secret
akeylessCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-secret",
Namespace: a.framework.Namespace.Name,
},
StringData: map[string]string{
"access-id": a.accessID,
"access-type": a.accessType,
"access-type-param": a.accessTypeParam,
},
}
err := a.framework.CRClient.Create(context.Background(), akeylessCreds)
Expect(err).ToNot(HaveOccurred())
// Creating Akeyless secret store
secretStore := &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: a.framework.Namespace.Name,
Namespace: a.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
Akeyless: &esv1alpha1.AkeylessProvider{
Auth: &esv1alpha1.AkeylessAuth{
SecretRef: esv1alpha1.AkeylessAuthSecretRef{
AccessID: esmeta.SecretKeySelector{
Name: "access-id-secret",
Key: "access-id",
},
AccessType: esmeta.SecretKeySelector{
Name: "access-type-secret",
Key: "access-type",
},
AccessTypeParam: esmeta.SecretKeySelector{
Name: "access-type-param-secert",
Key: "access-type-param",
},
},
},
},
},
},
}
err = a.framework.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
func (a *akeylessProvider) GetToken() (string, error) {
ctx := context.Background()
authBody := akeyless.NewAuthWithDefaults()
authBody.AccessId = akeyless.PtrString(a.accessID)
if a.accessType == "api_key" {
authBody.AccessKey = akeyless.PtrString(a.accessTypeParam)
} else if a.accessType == "k8s" {
jwtString, err := readK8SServiceAccountJWT()
if err != nil {
return "", fmt.Errorf("failed to read JWT with Kubernetes Auth from %v. error: %w", DefServiceAccountFile, err)
}
K8SAuthConfigName := a.accessTypeParam
authBody.AccessType = akeyless.PtrString(a.accessType)
authBody.K8sServiceAccountToken = akeyless.PtrString(jwtString)
authBody.K8sAuthConfigName = akeyless.PtrString(K8SAuthConfigName)
} else {
cloudID, err := a.getCloudID(a.accessType, a.accessTypeParam)
if err != nil {
return "", fmt.Errorf("Require Cloud ID " + err.Error())
}
authBody.AccessType = akeyless.PtrString(a.accessType)
authBody.CloudId = akeyless.PtrString(cloudID)
}
authOut, _, err := a.restAPIClient.Auth(ctx).Body(*authBody).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
}
return "", fmt.Errorf("authentication failed: %w", err)
}
token := authOut.GetToken()
return token, nil
}
func (a *akeylessProvider) getCloudID(provider, accTypeParam string) (string, error) {
var cloudID string
var err error
switch provider {
case "azure_ad":
cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
case "aws_iam":
cloudID, err = aws_cloud_id.GetCloudId()
case "gcp":
cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
default:
return "", fmt.Errorf("unable to determine provider: %s", provider)
}
return cloudID, err
}
// readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
func readK8SServiceAccountJWT() (string, error) {
data, err := os.Open(DefServiceAccountFile)
if err != nil {
return "", err
}
defer data.Close()
contentBytes, err := ioutil.ReadAll(data)
if err != nil {
return "", err
}
a := strings.TrimSpace(string(contentBytes))
return base64.StdEncoding.EncodeToString([]byte(a)), nil
}

16
go.mod
View file

@ -33,18 +33,21 @@ replace (
)
require (
cloud.google.com/go v0.65.0
cloud.google.com/go v0.81.0
github.com/Azure/azure-sdk-for-go v54.1.0+incompatible
github.com/Azure/go-autorest/autorest/azure/auth v0.5.7
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/IBM/go-sdk-core/v5 v5.5.0
github.com/IBM/secrets-manager-go-sdk v1.0.23
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2
github.com/akeylesslabs/akeyless-go/v2 v2.5.11
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1192
github.com/aws/aws-sdk-go v1.38.6
github.com/crossplane/crossplane-runtime v0.13.0
github.com/fatih/color v1.10.0 // indirect
github.com/frankban/quicktest v1.10.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v0.4.0
github.com/golang-jwt/jwt/v4 v4.1.0
github.com/google/go-cmp v0.5.5
@ -73,11 +76,12 @@ require (
go.uber.org/zap v1.17.0
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c
golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4 // indirect
google.golang.org/api v0.30.0
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a
google.golang.org/grpc v1.31.0
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c // indirect
golang.org/x/tools v0.1.7 // indirect
google.golang.org/api v0.45.0
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3
google.golang.org/grpc v1.37.0
honnef.co/go/tools v0.1.4 // indirect
k8s.io/api v0.21.3
k8s.io/apimachinery v0.21.3

99
go.sum
View file

@ -11,8 +11,13 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -72,6 +77,10 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2 h1:1h4udX3Y5KgSG0m4Th2bHfaYxZB9fbngiij9PrKEp6c=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2/go.mod h1:ionnWiARf5TYoFGuHS7syh51/7lYosZejpZbnECFJcU=
github.com/akeylesslabs/akeyless-go/v2 v2.5.11 h1:Z7xJAUPk1h4/YGS7yr/nx9RSMaSESzPwClqe21WFYHo=
github.com/akeylesslabs/akeyless-go/v2 v2.5.11/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -92,6 +101,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.38.6 h1:h0AKIaz/A1kEJ50HxCv7tL1GW+KbxYbp75+lZ/nvFOI=
github.com/aws/aws-sdk-go v1.38.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.23.0 h1:+E1q1LLSfHSDn/DzOtdJOX+pLZE2HiNV2yO5AjZINwM=
github.com/aws/aws-sdk-go-v2 v0.23.0/go.mod h1:2LhT7UgHOXK3UXONKI5OMgIyoQL6zTAw/jwIeX6yqzw=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -168,8 +179,9 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
@ -266,6 +278,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -281,6 +294,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
@ -295,6 +309,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -306,6 +321,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@ -313,6 +329,10 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -395,6 +415,7 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:W
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
@ -678,7 +699,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@ -689,8 +710,10 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -749,8 +772,9 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@ -760,6 +784,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -802,19 +827,30 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c h1:HiAZXo96zOhVhtFHchj/ojzoxCFiPrp9/j0GtS38V3g=
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78 h1:rPRtHfUb0UKZeZ6GH4K4Nt4YRbE9V1u+QZX5upZXqJQ=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -877,17 +913,26 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs=
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
@ -965,13 +1010,18 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4 h1:cYSqdOzmV9wJ7lWurRAws06Dmif0Wv6UL4gQLlz+im0=
golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -995,8 +1045,14 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.45.0 h1:pqMffJFLBVUDIoYsHcqtxgQVTsmxMDpYLOc5MT4Jrww=
google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1035,9 +1091,20 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3 h1:K+7Ig5hjiLVA/i1UFUUbCGimWz5/Ey0lAQjT3QiLaPY=
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=

View file

@ -45,6 +45,7 @@ nav:
- Secrets Manager: provider-google-secrets-manager.md
- IBM:
- Secrets Manager: provider-ibm-secrets-manager.md
- Akeyless: provider-akeyless.md
- HashiCorp Vault: provider-hashicorp-vault.md
- Yandex:
- Lockbox: provider-yandex-lockbox.md

View file

@ -0,0 +1,155 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"context"
"encoding/json"
"fmt"
"strconv"
"github.com/akeylesslabs/akeyless-go/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"github.com/external-secrets/external-secrets/pkg/utils"
)
const (
defaultAPIUrl = "https://api.akeyless.io"
)
// Provider satisfies the provider interface.
type Provider struct{}
// akeylessBase satisfies the provider.SecretsClient interface.
type akeylessBase struct {
kube client.Client
store esv1alpha1.GenericStore
namespace string
akeylessGwAPIURL string
RestAPI *akeyless.V2ApiService
}
type Akeyless struct {
Client akeylessVaultInterface
}
type akeylessVaultInterface interface {
GetSecretByType(secretName, token string, version int32) (string, error)
TokenFromSecretRef(ctx context.Context) (string, error)
}
func init() {
schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
Akeyless: &esv1alpha1.AkeylessProvider{},
})
}
// NewClient constructs a new secrets client based on the provided store.
func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
return newClient(ctx, store, kube, namespace)
}
func newClient(_ context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
akl := &akeylessBase{
kube: kube,
store: store,
namespace: namespace,
}
spec, err := GetAKeylessProvider(store)
if err != nil {
return nil, err
}
akeylessGwAPIURL := defaultAPIUrl
if spec != nil && spec.AkeylessGWApiURL != nil && *spec.AkeylessGWApiURL != "" {
akeylessGwAPIURL = getV2Url(*spec.AkeylessGWApiURL)
}
if spec.Auth == nil {
return nil, fmt.Errorf("missing Auth in store config")
}
RestAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
Servers: []akeyless.ServerConfiguration{
{
URL: akeylessGwAPIURL,
},
},
}).V2Api
akl.akeylessGwAPIURL = akeylessGwAPIURL
akl.RestAPI = RestAPIClient
return &Akeyless{Client: akl}, nil
}
func (a *Akeyless) Close(ctx context.Context) error {
return nil
}
// Implements store.Client.GetSecret Interface.
// Retrieves a secret with the secret name defined in ref.Name.
func (a *Akeyless) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(a.Client) {
return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
}
token, err := a.Client.TokenFromSecretRef(ctx)
if err != nil {
return nil, err
}
version := int32(0)
if ref.Version != "" {
i, err := strconv.ParseInt(ref.Version, 10, 32)
if err == nil {
version = int32(i)
}
}
value, err := a.Client.GetSecretByType(ref.Key, token, version)
if err != nil {
return nil, err
}
return []byte(value), nil
}
// Implements store.Client.GetSecretMap Interface.
// New version of GetSecretMap.
func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if utils.IsNil(a.Client) {
return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
}
val, err := a.GetSecret(ctx, ref)
if err != nil {
return nil, err
}
// Maps the json data to a string:string map
kv := make(map[string]string)
err = json.Unmarshal(val, &kv)
if err != nil {
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
}
// Converts values in K:V pairs into bytes, while leaving keys as strings
secretData := make(map[string][]byte)
for k, v := range kv {
secretData[k] = []byte(v)
}
return secretData, nil
}

View file

@ -0,0 +1,250 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
aws_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/aws"
azure_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/azure"
gcp_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/gcp"
"github.com/akeylesslabs/akeyless-go/v2"
)
var apiErr akeyless.GenericOpenAPIError
const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
func (a *akeylessBase) GetToken(accessID, accType, accTypeParam string) (string, error) {
ctx := context.Background()
authBody := akeyless.NewAuthWithDefaults()
authBody.AccessId = akeyless.PtrString(accessID)
if accType == "api_key" || accType == "access_key" {
authBody.AccessKey = akeyless.PtrString(accTypeParam)
} else if accType == "k8s" {
jwtString, err := readK8SServiceAccountJWT()
if err != nil {
return "", fmt.Errorf("failed to read JWT with Kubernetes Auth from %v. error: %w", DefServiceAccountFile, err)
}
K8SAuthConfigName := accTypeParam
authBody.AccessType = akeyless.PtrString(accType)
authBody.K8sServiceAccountToken = akeyless.PtrString(jwtString)
authBody.K8sAuthConfigName = akeyless.PtrString(K8SAuthConfigName)
} else {
cloudID, err := a.getCloudID(accType, accTypeParam)
if err != nil {
return "", fmt.Errorf("Require Cloud ID " + err.Error())
}
authBody.AccessType = akeyless.PtrString(accType)
authBody.CloudId = akeyless.PtrString(cloudID)
}
authOut, _, err := a.RestAPI.Auth(ctx).Body(*authBody).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
}
return "", fmt.Errorf("authentication failed: %w", err)
}
token := authOut.GetToken()
return token, nil
}
func (a *akeylessBase) GetSecretByType(secretName, token string, version int32) (string, error) {
item, err := a.DescribeItem(secretName, token)
if err != nil {
return "", err
}
secretType := item.GetItemType()
switch secretType {
case "STATIC_SECRET":
return a.GetStaticSecret(secretName, token, version)
case "DYNAMIC_SECRET":
return a.GetDynamicSecrets(secretName, token)
case "ROTATED_SECRET":
return a.GetRotatedSecrets(secretName, token, version)
default:
return "", fmt.Errorf("invalid item type: %v", secretType)
}
}
func (a *akeylessBase) DescribeItem(itemName, token string) (*akeyless.Item, error) {
ctx := context.Background()
body := akeyless.DescribeItem{
Name: itemName,
}
if strings.HasPrefix(token, "u-") {
body.UidToken = &token
} else {
body.Token = &token
}
gsvOut, _, err := a.RestAPI.DescribeItem(ctx).Body(body).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return nil, fmt.Errorf("can't describe item: %v", string(apiErr.Body()))
}
return nil, fmt.Errorf("can't describe item: %w", err)
}
return &gsvOut, nil
}
func (a *akeylessBase) GetRotatedSecrets(secretName, token string, version int32) (string, error) {
ctx := context.Background()
body := akeyless.GetRotatedSecretValue{
Names: secretName,
Version: &version,
}
if strings.HasPrefix(token, "u-") {
body.UidToken = &token
} else {
body.Token = &token
}
gsvOut, _, err := a.RestAPI.GetRotatedSecretValue(ctx).Body(body).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
}
return "", fmt.Errorf("can't get rotated secret value: %w", err)
}
val, ok := gsvOut["value"]
if ok {
if _, ok := val["payload"]; ok {
return fmt.Sprintf("%v", val["payload"]), nil
} else if _, ok := val["target_value"]; ok {
out, err := json.Marshal(val["target_value"])
if err != nil {
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
}
return string(out), nil
} else {
out, err := json.Marshal(val)
if err != nil {
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
}
return string(out), nil
}
}
out, err := json.Marshal(gsvOut)
if err != nil {
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
}
return string(out), nil
}
func (a *akeylessBase) GetDynamicSecrets(secretName, token string) (string, error) {
ctx := context.Background()
body := akeyless.GetDynamicSecretValue{
Name: secretName,
}
if strings.HasPrefix(token, "u-") {
body.UidToken = &token
} else {
body.Token = &token
}
gsvOut, _, err := a.RestAPI.GetDynamicSecretValue(ctx).Body(body).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
}
return "", fmt.Errorf("can't get dynamic secret value: %w", err)
}
out, err := json.Marshal(gsvOut)
if err != nil {
return "", fmt.Errorf("can't marshal dynamic secret value: %w", err)
}
return string(out), nil
}
func (a *akeylessBase) GetStaticSecret(secretName, token string, version int32) (string, error) {
ctx := context.Background()
gsvBody := akeyless.GetSecretValue{
Names: []string{secretName},
Version: &version,
}
if strings.HasPrefix(token, "u-") {
gsvBody.UidToken = &token
} else {
gsvBody.Token = &token
}
gsvOut, _, err := a.RestAPI.GetSecretValue(ctx).Body(gsvBody).Execute()
if err != nil {
if errors.As(err, &apiErr) {
return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
}
return "", fmt.Errorf("can't get secret value: %w", err)
}
val, ok := gsvOut[secretName]
if !ok {
return "", fmt.Errorf("can't get secret: %v", secretName)
}
return val, nil
}
func (a *akeylessBase) getCloudID(provider, accTypeParam string) (string, error) {
var cloudID string
var err error
switch provider {
case "azure_ad":
cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
case "aws_iam":
cloudID, err = aws_cloud_id.GetCloudId()
case "gcp":
cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
default:
return "", fmt.Errorf("unable to determine provider: %s", provider)
}
return cloudID, err
}
// readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
func readK8SServiceAccountJWT() (string, error) {
data, err := os.Open(DefServiceAccountFile)
if err != nil {
return "", err
}
defer data.Close()
contentBytes, err := ioutil.ReadAll(data)
if err != nil {
return "", err
}
a := strings.TrimSpace(string(contentBytes))
return base64.StdEncoding.EncodeToString([]byte(a)), nil
}

View file

@ -0,0 +1,168 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"context"
"fmt"
"reflect"
"strings"
"testing"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
fakeakeyless "github.com/external-secrets/external-secrets/pkg/provider/akeyless/fake"
)
type akeylessTestCase struct {
mockClient *fakeakeyless.AkeylessMockClient
apiInput *fakeakeyless.Input
apiOutput *fakeakeyless.Output
ref *esv1alpha1.ExternalSecretDataRemoteRef
expectError string
expectedSecret string
// for testing secretmap
expectedData map[string][]byte
}
func makeValidAkeylessTestCase() *akeylessTestCase {
smtc := akeylessTestCase{
mockClient: &fakeakeyless.AkeylessMockClient{},
apiInput: makeValidInput(),
ref: makeValidRef(),
apiOutput: makeValidOutput(),
expectError: "",
expectedSecret: "",
expectedData: map[string][]byte{},
}
smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput)
return &smtc
}
func makeValidRef() *esv1alpha1.ExternalSecretDataRemoteRef {
return &esv1alpha1.ExternalSecretDataRemoteRef{
Key: "test-secret",
Version: "1",
}
}
func makeValidInput() *fakeakeyless.Input {
return &fakeakeyless.Input{
SecretName: "name",
Version: 0,
Token: "token",
}
}
func makeValidOutput() *fakeakeyless.Output {
return &fakeakeyless.Output{
Value: "secret-val",
Err: nil,
}
}
func makeValidAkeylessTestCaseCustom(tweaks ...func(smtc *akeylessTestCase)) *akeylessTestCase {
smtc := makeValidAkeylessTestCase()
for _, fn := range tweaks {
fn(smtc)
}
smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput)
return smtc
}
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *akeylessTestCase) {
smtc.apiOutput.Err = fmt.Errorf("oh no")
smtc.expectError = "oh no"
}
var setNilMockClient = func(smtc *akeylessTestCase) {
smtc.mockClient = nil
smtc.expectError = errUninitalizedAkeylessProvider
}
func TestAkeylessGetSecret(t *testing.T) {
secretValue := "changedvalue"
// good case: default version is set
// key is passed in, output is sent back
setSecretString := func(smtc *akeylessTestCase) {
smtc.apiOutput = &fakeakeyless.Output{
Value: secretValue,
Err: nil,
}
smtc.expectedSecret = secretValue
}
successCases := []*akeylessTestCase{
makeValidAkeylessTestCaseCustom(setAPIErr),
makeValidAkeylessTestCaseCustom(setSecretString),
makeValidAkeylessTestCaseCustom(setNilMockClient),
}
sm := Akeyless{}
for k, v := range successCases {
sm.Client = v.mockClient
fmt.Println(*v.ref)
out, err := sm.GetSecret(context.Background(), *v.ref)
if !ErrorContains(err, v.expectError) {
t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
}
if string(out) != v.expectedSecret {
t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
}
}
}
func TestGetSecretMap(t *testing.T) {
// good case: default version & deserialization
setDeserialization := func(smtc *akeylessTestCase) {
smtc.apiOutput.Value = `{"foo":"bar"}`
smtc.expectedData["foo"] = []byte("bar")
}
// bad case: invalid json
setInvalidJSON := func(smtc *akeylessTestCase) {
smtc.apiOutput.Value = `-----------------`
smtc.expectError = "unable to unmarshal secret"
}
successCases := []*akeylessTestCase{
makeValidAkeylessTestCaseCustom(setDeserialization),
makeValidAkeylessTestCaseCustom(setInvalidJSON),
makeValidAkeylessTestCaseCustom(setAPIErr),
makeValidAkeylessTestCaseCustom(setNilMockClient),
}
sm := Akeyless{}
for k, v := range successCases {
sm.Client = v.mockClient
out, err := sm.GetSecretMap(context.Background(), *v.ref)
if !ErrorContains(err, v.expectError) {
t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
}
if err == nil && !reflect.DeepEqual(out, v.expectedData) {
t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
}
}
}
func ErrorContains(out error, want string) bool {
if out == nil {
return want == ""
}
if want == "" {
return false
}
return strings.Contains(out.Error(), want)
}

View file

@ -0,0 +1,103 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
)
const (
errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing Akeyless AccessID Namespace"
errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing Akeyless AccessType Namespace"
errFetchAKIDSecret = "could not fetch accessID secret: %w"
errFetchSAKSecret = "could not fetch AccessType secret: %w"
errMissingSAK = "missing SecretAccessKey"
errMissingAKID = "missing AccessKeyID"
)
func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) {
prov, err := GetAKeylessProvider(a.store)
if err != nil {
return "", err
}
ke := client.ObjectKey{
Name: prov.Auth.SecretRef.AccessID.Name,
Namespace: a.namespace, // default to ExternalSecret namespace
}
// only ClusterStore is allowed to set namespace (and then it's required)
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
if prov.Auth.SecretRef.AccessID.Namespace == nil {
return "", fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
}
ke.Namespace = *prov.Auth.SecretRef.AccessID.Namespace
}
accessIDSecret := v1.Secret{}
err = a.kube.Get(ctx, ke, &accessIDSecret)
if err != nil {
return "", fmt.Errorf(errFetchAKIDSecret, err)
}
ke = client.ObjectKey{
Name: prov.Auth.SecretRef.AccessType.Name,
Namespace: a.namespace, // default to ExternalSecret namespace
}
// only ClusterStore is allowed to set namespace (and then it's required)
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
if prov.Auth.SecretRef.AccessType.Namespace == nil {
return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
}
ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace
}
accessTypeSecret := v1.Secret{}
err = a.kube.Get(ctx, ke, &accessTypeSecret)
if err != nil {
return "", fmt.Errorf(errFetchSAKSecret, err)
}
ke = client.ObjectKey{
Name: prov.Auth.SecretRef.AccessTypeParam.Name,
Namespace: a.namespace, // default to ExternalSecret namespace
}
// only ClusterStore is allowed to set namespace (and then it's required)
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
if prov.Auth.SecretRef.AccessType.Namespace == nil {
return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
}
ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace
}
accessTypeParamSecret := v1.Secret{}
err = a.kube.Get(ctx, ke, &accessTypeParamSecret)
if err != nil {
return "", fmt.Errorf(errFetchSAKSecret, err)
}
accessID := string(accessIDSecret.Data[prov.Auth.SecretRef.AccessID.Key])
accessType := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessType.Key])
accessTypeParam := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessTypeParam.Key])
if accessID == "" {
return "", fmt.Errorf(errMissingSAK)
}
if accessType == "" {
return "", fmt.Errorf(errMissingAKID)
}
return a.GetToken(accessID, accessType, accessTypeParam)
}

View file

@ -0,0 +1,49 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fake
import (
"context"
)
type AkeylessMockClient struct {
getSecret func(secretName, token string, version int32) (string, error)
}
func (mc *AkeylessMockClient) TokenFromSecretRef(ctx context.Context) (string, error) {
return "newToken", nil
}
func (mc *AkeylessMockClient) GetSecretByType(secretName, token string, version int32) (string, error) {
return mc.getSecret(secretName, token, version)
}
func (mc *AkeylessMockClient) WithValue(in *Input, out *Output) {
if mc != nil {
mc.getSecret = func(secretName, token string, version int32) (string, error) {
return out.Value, out.Err
}
}
}
type Input struct {
SecretName string
Token string
Version int32
}
type Output struct {
Value string
Err error
}

View file

@ -0,0 +1,99 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akeyless
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
)
const (
errNilStore = "found nil store"
errMissingStoreSpec = "store is missing spec"
errMissingProvider = "storeSpec is missing provider"
errInvalidProvider = "invalid provider spec. Missing Akeyless field in store %s"
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
errUninitalizedAkeylessProvider = "provider akeyless is not initialized"
)
// GetAKeylessProvider does the necessary nil checks and returns the akeyless provider or an error.
func GetAKeylessProvider(store esv1alpha1.GenericStore) (*esv1alpha1.AkeylessProvider, error) {
if store == nil {
return nil, fmt.Errorf(errNilStore)
}
spc := store.GetSpec()
if spc == nil {
return nil, fmt.Errorf(errMissingStoreSpec)
}
if spc.Provider == nil {
return nil, fmt.Errorf(errMissingProvider)
}
prov := spc.Provider.Akeyless
if prov == nil {
return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
}
return prov, nil
}
func getV2Url(path string) string {
// add check if not v2
rebody := sendReq(path)
if strings.Contains(rebody, "unknown command") {
return path
}
if strings.HasSuffix(path, "/v2") {
return path
}
url, err := url.Parse(path)
if err != nil {
return path
}
if strings.HasSuffix(url.Host, "/v2") {
return path
}
url.Host += "/v2"
p := url.Scheme + "://" + url.Host
if url.Port() != "" {
p = p + ":" + url.Port()
}
return p
}
func sendReq(url string) string {
req, err := http.NewRequest("POST", url, nil)
if err != nil {
return ""
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
return ""
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body)
}

View file

@ -17,6 +17,7 @@ package register
// packages imported here are registered to the controller schema.
// nolint:revive
import (
_ "github.com/external-secrets/external-secrets/pkg/provider/akeyless"
_ "github.com/external-secrets/external-secrets/pkg/provider/alibaba"
_ "github.com/external-secrets/external-secrets/pkg/provider/aws"
_ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault"