mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Adde Akeyless
This commit is contained in:
parent
6c80f445e4
commit
4a5877d926
20 changed files with 1367 additions and 7 deletions
|
@ -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 Secrets Manager](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
|
||||
|
|
42
apis/externalsecrets/v1alpha1/secretstore_akeyless_types.go
Normal file
42
apis/externalsecrets/v1alpha1/secretstore_akeyless_types.go
Normal 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"`
|
||||
}
|
|
@ -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"`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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-conf-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
|
||||
|
|
|
@ -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-conf-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
|
||||
|
|
66
docs/provider-akeyelss.md
Normal file
66
docs/provider-akeyelss.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
## 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 are their params:
|
||||
|
||||
| accessType | accessTypeParam |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `api_key` | The access key. |
|
||||
| `k8s` | The k8s configuration name |
|
||||
| `aws_iam` | - |
|
||||
| `gcp` | The gcp audience |
|
||||
| `azure_ad` | azure object id |
|
||||
|
||||
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 variable 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 project variable 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}'
|
||||
```
|
12
docs/snippets/akeyless-credentials-secret.yaml
Normal file
12
docs/snippets/akeyless-credentials-secret.yaml
Normal 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
|
||||
|
||||
|
||||
|
18
docs/snippets/akeyless-external-secret-json.yaml
Normal file
18
docs/snippets/akeyless-external-secret-json.yaml
Normal 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
|
||||
|
||||
# each secret name in the KV will be used as the secret key in the SECRET k8s target object
|
||||
dataFrom:
|
||||
- key: "my-app" # Key of the variable on Gitlab
|
19
docs/snippets/akeyless-external-secret.yaml
Normal file
19
docs/snippets/akeyless-external-secret.yaml
Normal 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: my-app # Key of the variable on Gitlab
|
20
docs/snippets/akeyless-secret-store.yaml
Normal file
20
docs/snippets/akeyless-secret-store.yaml
Normal 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: "http://host.docker.internal:8080" #"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
|
19
go.mod
19
go.mod
|
@ -33,18 +33,22 @@ 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/aws/aws-sdk-go-v2 v0.23.0
|
||||
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 v3.2.2+incompatible
|
||||
github.com/google/go-cmp v0.5.5
|
||||
|
@ -57,7 +61,7 @@ require (
|
|||
github.com/hashicorp/vault/api v1.0.5-0.20210224012239-b540be4b7ec4
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.1
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.16.0
|
||||
github.com/oracle/oci-go-sdk/v45 v45.2.0
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
||||
|
@ -73,11 +77,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
|
||||
|
|
85
go.sum
85
go.sum
|
@ -13,6 +13,12 @@ cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZ
|
|||
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 +78,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 +102,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=
|
||||
|
@ -170,6 +182,8 @@ github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq
|
|||
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=
|
||||
|
@ -210,6 +224,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
|||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
|
@ -265,6 +280,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=
|
||||
|
@ -280,6 +296,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=
|
||||
|
@ -294,6 +311,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=
|
||||
|
@ -305,6 +323,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=
|
||||
|
@ -312,6 +331,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=
|
||||
|
@ -394,6 +417,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=
|
||||
|
@ -535,6 +559,8 @@ github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
|
|||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
|
@ -677,6 +703,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
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,6 +716,9 @@ 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,6 +779,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
|||
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/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=
|
||||
|
@ -758,6 +789,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=
|
||||
|
@ -800,19 +832,34 @@ 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-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-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-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-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-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-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=
|
||||
|
@ -875,17 +922,29 @@ 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-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-20210330210617-4fbd30eecc44/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=
|
||||
|
@ -963,13 +1022,20 @@ 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,6 +1061,13 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
|||
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=
|
||||
|
@ -1033,9 +1106,21 @@ 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-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
|
||||
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=
|
||||
|
|
159
pkg/provider/akeyless/akeyless.go
Normal file
159
pkg/provider/akeyless/akeyless.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/akeylesslabs/akeyless-go/v2"
|
||||
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 (
|
||||
defaultObjType = "secret"
|
||||
defaultAPIUrl = "https://api.akeyless.io"
|
||||
)
|
||||
|
||||
// Provider satisfies the provider interface.
|
||||
type Provider struct{}
|
||||
|
||||
// Akeyless 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(ctx 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
|
||||
}
|
268
pkg/provider/akeyless/akeyless_api.go
Normal file
268
pkg/provider/akeyless/akeyless_api.go
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
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: %v", DefServiceAccountFile, err.Error())
|
||||
}
|
||||
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: %v", 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: %v", 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: %v", 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: %v", err)
|
||||
}
|
||||
return string(out), nil
|
||||
} else {
|
||||
out, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't marshal rotated secret value: %v", err)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
}
|
||||
out, err := json.Marshal(gsvOut)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't marshal rotated secret value: %v", 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: %v", err)
|
||||
}
|
||||
|
||||
out, err := json.Marshal(gsvOut)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't marshal dynamic secret value: %v", err)
|
||||
}
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func (a *AkeylessBase) getSecretVersionByType(secretType, secretName, token string, version int32) (string, error) {
|
||||
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) 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: %v", err)
|
||||
}
|
||||
val, ok := gsvOut[secretName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("can't get secret: %v",secretName )
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (a *AkeylessBase) getCloudId(provider string, 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
|
||||
}
|
168
pkg/provider/akeyless/akeyless_test.go
Normal file
168
pkg/provider/akeyless/akeyless_test.go
Normal 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)
|
||||
}
|
103
pkg/provider/akeyless/auth.go
Normal file
103
pkg/provider/akeyless/auth.go
Normal 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"
|
||||
|
||||
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
49
pkg/provider/akeyless/fake/fake.go
Normal file
49
pkg/provider/akeyless/fake/fake.go
Normal 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
|
||||
}
|
99
pkg/provider/akeyless/utils.go
Normal file
99
pkg/provider/akeyless/utils.go
Normal 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
|
||||
} else {
|
||||
url.Host = 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)
|
||||
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)
|
||||
}
|
|
@ -17,6 +17,7 @@ package register
|
|||
// packages imported here are registered to the controller schema.
|
||||
// nolint:golint
|
||||
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"
|
||||
|
|
Loading…
Reference in a new issue