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

GitHub provider (supersedes #3014) (#3115)

* github provider signed, supersedes #3014

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* tests pass, + crd + docs

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* fix sonarLint alert

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* refactoring, replace secretStore with generator

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* cosmetics + tst + lint pass

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* docs

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* clean-up + lint + test

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

* small refactor, fix issues left in comments

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>

---------

Signed-off-by: Mike Serchenia <michael_serchenia@epam.com>
This commit is contained in:
Michael Serchenia 2024-04-03 02:19:57 -05:00 committed by GitHub
parent ecbbdf4b5b
commit 84731616f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 764 additions and 2 deletions

View file

@ -0,0 +1,59 @@
/*
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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
)
type GithubAccessTokenSpec struct {
// URL configures the Github instance URL. Defaults to https://github.com/.
URL string `json:"url,omitempty"`
AppID string `json:"appID"`
InstallID string `json:"installID"`
// Auth configures how ESO authenticates with a Github instance.
Auth GithubAuth `json:"auth"`
}
type GithubAuth struct {
PrivatKey GithubSecretRef `json:"privatKey"`
}
type GithubSecretRef struct {
SecretRef esmeta.SecretKeySelector `json:"secretRef"`
}
// GithubAccessToken generates ghs_ accessToken
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced,categories={githubaccesstoken},shortName=githubaccesstoken
type GithubAccessToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec GithubAccessTokenSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
// GithubAccessToken contains a list of ExternalSecret resources.
type GithubAccessTokenList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []GithubAccessToken `json:"items"`
}

View file

@ -92,9 +92,18 @@ var (
VaultDynamicSecretGroupVersionKind = SchemeGroupVersion.WithKind(VaultDynamicSecretKind)
)
// GithubAccessToken type metadata.
var (
GithubAccessTokenKind = reflect.TypeOf(GithubAccessToken{}).Name()
GithubAccessTokenGroupKind = schema.GroupKind{Group: Group, Kind: GithubAccessTokenKind}.String()
GithubAccessTokenKindAPIVersion = GithubAccessTokenKind + "." + SchemeGroupVersion.String()
GithubAccessTokenGroupVersionKind = SchemeGroupVersion.WithKind(GithubAccessTokenKind)
)
func init() {
SchemeBuilder.Register(&ECRAuthorizationToken{}, &ECRAuthorizationToken{})
SchemeBuilder.Register(&GCRAccessToken{}, &GCRAccessTokenList{})
SchemeBuilder.Register(&GithubAccessToken{}, &GithubAccessTokenList{})
SchemeBuilder.Register(&ACRAccessToken{}, &ACRAccessTokenList{})
SchemeBuilder.Register(&Fake{}, &FakeList{})
SchemeBuilder.Register(&VaultDynamicSecret{}, &VaultDynamicSecretList{})

View file

@ -566,6 +566,112 @@ func (in *GCRAccessTokenSpec) DeepCopy() *GCRAccessTokenSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAccessToken) DeepCopyInto(out *GithubAccessToken) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubAccessToken.
func (in *GithubAccessToken) DeepCopy() *GithubAccessToken {
if in == nil {
return nil
}
out := new(GithubAccessToken)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *GithubAccessToken) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAccessTokenList) DeepCopyInto(out *GithubAccessTokenList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]GithubAccessToken, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubAccessTokenList.
func (in *GithubAccessTokenList) DeepCopy() *GithubAccessTokenList {
if in == nil {
return nil
}
out := new(GithubAccessTokenList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *GithubAccessTokenList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAccessTokenSpec) DeepCopyInto(out *GithubAccessTokenSpec) {
*out = *in
in.Auth.DeepCopyInto(&out.Auth)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubAccessTokenSpec.
func (in *GithubAccessTokenSpec) DeepCopy() *GithubAccessTokenSpec {
if in == nil {
return nil
}
out := new(GithubAccessTokenSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAuth) DeepCopyInto(out *GithubAuth) {
*out = *in
in.PrivatKey.DeepCopyInto(&out.PrivatKey)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubAuth.
func (in *GithubAuth) DeepCopy() *GithubAuth {
if in == nil {
return nil
}
out := new(GithubAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubSecretRef) DeepCopyInto(out *GithubSecretRef) {
*out = *in
in.SecretRef.DeepCopyInto(&out.SecretRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubSecretRef.
func (in *GithubSecretRef) DeepCopy() *GithubSecretRef {
if in == nil {
return nil
}
out := new(GithubSecretRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Password) DeepCopyInto(out *Password) {
*out = *in

View file

@ -0,0 +1,91 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
name: githubaccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- githubaccesstoken
kind: GithubAccessToken
listKind: GithubAccessTokenList
plural: githubaccesstokens
shortNames:
- githubaccesstoken
singular: githubaccesstoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: GithubAccessToken generates ghs_ accessToken
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
appID:
type: string
auth:
description: Auth configures how ESO authenticates with a Github instance.
properties:
privatKey:
properties:
secretRef:
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
required:
- secretRef
type: object
required:
- privatKey
type: object
installID:
type: string
url:
description: URL configures the Github instance URL. Defaults to https://github.com/.
type: string
required:
- appID
- auth
- installID
type: object
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -53,6 +53,7 @@ rules:
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
- "passwords"
- "vaultdynamicsecrets"
verbs:
@ -145,6 +146,7 @@ rules:
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
- "passwords"
- "vaultdynamicsecrets"
verbs:
@ -188,6 +190,7 @@ rules:
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
- "passwords"
- "vaultdynamicsecrets"
verbs:

View file

@ -10512,6 +10512,107 @@ spec:
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
name: githubaccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- githubaccesstoken
kind: GithubAccessToken
listKind: GithubAccessTokenList
plural: githubaccesstokens
shortNames:
- githubaccesstoken
singular: githubaccesstoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: GithubAccessToken generates ghs_ accessToken
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
appID:
type: string
auth:
description: Auth configures how ESO authenticates with a Github instance.
properties:
privatKey:
properties:
secretRef:
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
required:
- secretRef
type: object
required:
- privatKey
type: object
installID:
type: string
url:
description: URL configures the Github instance URL. Defaults to https://github.com/.
type: string
required:
- appID
- auth
- installID
type: object
type: object
served: true
storage: true
subresources:
status: {}
conversion:
strategy: Webhook
webhook:
conversionReviewVersions:
- v1
clientConfig:
service:
name: kubernetes
namespace: default
path: /convert
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0

View file

@ -0,0 +1,14 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: github-auth-token
spec:
refreshInterval: "30m"
target:
name: github-auth-token # Name for the secret to be created on the cluster
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: GithubAccessToken
name: github-auth-token

View file

@ -0,0 +1,20 @@
# 1. Register Github app https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app#registering-a-github-app
# `App ID: 123456` will be displayed after you create an app. Next on the bottom of the page, you'll find `Generate a private key` button.
# 2. Get privateKey https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps#generating-private-keys put it in e.g `github-app-pem` k8s secret
# 3. Set permissions for the app, e.g if you want to push OCI images to ghr set RW for packages https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app#choosing-permissions-for-rest-api-access
# 4. Install your Github app https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app
# 5. Get `installID` https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app#generating-an-installation-access-token (2)
---
apiVersion: generators.external-secrets.io/v1alpha1
kind: GithubAccessToken
metadata:
name: github-auth-token
spec:
appID: "0000000" # (1)
installID: "00000000" # (5)
url: "" # (Default https://api.github.com.)
auth:
privatKey:
secretRef:
name: github-app-pem # (2)
key: key

View file

@ -1,4 +1,4 @@
apiVersion: external-secrets.io/v1beta1
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: find-by-tags
@ -12,4 +12,4 @@ spec:
dataFrom:
- find:
name:
regexp: "key"
regexp: "key"

View file

@ -0,0 +1,169 @@
/*
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 github
import (
"context"
"crypto/rsa"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/golang-jwt/jwt/v5"
corev1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
)
type Generator struct {
httpClient *http.Client
}
type Github struct {
HTTP *http.Client
Kube client.Client
Namespace string
URL string
InstallTkn string
}
const (
defaultLoginUsername = "token"
defaultGithubAPI = "https://api.github.com"
errNoSpec = "no config spec provided"
errParseSpec = "unable to parse spec: %w"
errGetToken = "unable to get authorization token: %w"
contextTimeout = 30 * time.Second
httpClientTimeout = 5 * time.Second
)
func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, error) {
return g.generate(
ctx,
jsonSpec,
kube,
namespace,
)
}
func (g *Generator) generate(
ctx context.Context,
jsonSpec *apiextensions.JSON,
kube client.Client,
namespace string) (map[string][]byte, error) {
if jsonSpec == nil {
return nil, fmt.Errorf(errNoSpec)
}
ctx, cancel := context.WithTimeout(ctx, contextTimeout)
defer cancel()
gh, err := newGHClient(ctx, kube, namespace, g.httpClient, jsonSpec)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
// Github api expects POST request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, gh.URL, http.NoBody)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Add("Authorization", "Bearer "+gh.InstallTkn)
req.Header.Add("Accept", "application/vnd.github.v3+json")
resp, err := gh.HTTP.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing request: %w", err)
}
defer resp.Body.Close()
// git access token
var gat map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&gat); err != nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
return nil, fmt.Errorf("error decoding response: %w", err)
}
accessToken, ok := gat["token"].(string)
if !ok {
return nil, fmt.Errorf("token isn't a string or token key doesn't exist")
}
return map[string][]byte{
defaultLoginUsername: []byte(accessToken),
}, nil
}
func newGHClient(ctx context.Context, k client.Client, n string, hc *http.Client,
js *apiextensions.JSON) (*Github, error) {
if hc == nil {
hc = &http.Client{
Timeout: httpClientTimeout,
}
}
res, err := parseSpec(js.Raw)
if err != nil {
return nil, fmt.Errorf(errParseSpec, err)
}
gh := &Github{Kube: k, Namespace: n, HTTP: hc}
ghPath := fmt.Sprintf("/app/installations/%s/access_tokens", res.Spec.InstallID)
gh.URL = defaultGithubAPI + ghPath
if res.Spec.URL != "" {
gh.URL = res.Spec.URL + ghPath
}
secret := &corev1.Secret{}
if err := gh.Kube.Get(ctx, client.ObjectKey{Name: res.Spec.Auth.PrivatKey.SecretRef.Name, Namespace: n}, secret); err != nil {
return nil, fmt.Errorf("error getting GH pem from secret:%w", err)
}
pk, err := jwt.ParseRSAPrivateKeyFromPEM(secret.Data[res.Spec.Auth.PrivatKey.SecretRef.Key])
if err != nil {
return nil, fmt.Errorf("error parsing RSA private key: %w", err)
}
if gh.InstallTkn, err = GetInstallationToken(pk, res.Spec.AppID); err != nil {
return nil, fmt.Errorf("can't get InstallationToken: %w", err)
}
return gh, nil
}
// Get github installation token.
func GetInstallationToken(key *rsa.PrivateKey, aid string) (string, error) {
claims := jwt.RegisteredClaims{
Issuer: aid,
IssuedAt: jwt.NewNumericDate(time.Now().Add(-time.Second * 10)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * 300)),
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
signedToken, err := token.SignedString(key)
if err != nil {
return "", fmt.Errorf("error signing token: %w", err)
}
return signedToken, nil
}
func parseSpec(data []byte) (*genv1alpha1.GithubAccessToken, error) {
var spec genv1alpha1.GithubAccessToken
err := yaml.Unmarshal(data, &spec)
return &spec, err
}
func init() {
genv1alpha1.Register(genv1alpha1.GithubAccessTokenKind, &Generator{})
}

View file

@ -0,0 +1,138 @@
/*
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 github
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
)
const (
tstCrtName = "github_test.pem"
)
func testHTTPSrv(t *testing.T, r []byte) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "POST", req.Method, "Expected POST request")
assert.Empty(t, req.Body)
assert.NotEmpty(t, req.Header.Get("Authorization"))
assert.Equal(t, "application/vnd.github.v3+json", req.Header.Get("Accept"))
// Send response to be tested
rw.Write(r)
}))
}
func TestGenerate(t *testing.T) {
type args struct {
ctx context.Context
jsonSpec *apiextensions.JSON
kube client.Client
namespace string
}
pem, err := os.ReadFile(tstCrtName)
assert.NoError(t, err, "Should not error when reading privatKey")
validResponce := []byte(`{
"token": "ghs_16C7e42F292c6912E7710c838347Ae178B4a",
"expires_at": "2016-07-11T22:14:10Z",
"permissions": {
"issues": "write",
"contents": "read"
},
"repository_selection": "selected"
}`)
server := testHTTPSrv(t, validResponce)
tests := []struct {
name string
g *Generator
args args
want map[string][]byte
wantErr bool
}{
{
name: "nil spec",
args: args{
jsonSpec: nil,
},
wantErr: true,
},
{
name: "full spec",
args: args{
ctx: context.TODO(),
namespace: "foo",
kube: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "testName",
Namespace: "foo",
},
Data: map[string][]byte{
"privatKey": pem,
},
}).Build(),
jsonSpec: &apiextensions.JSON{
Raw: []byte(fmt.Sprintf(`apiVersion: generators.external-secrets.io/v1alpha1
kind: GithubToken
spec:
appID: "0000000"
installID: "00000000"
URL: %q
auth:
privatKey:
secretRef:
name: "testName"
namespace: "foo"
key: "privatKey"`, server.URL)),
},
},
want: map[string][]byte{
"token": []byte("ghs_16C7e42F292c6912E7710c838347Ae178B4a"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := &Generator{httpClient: server.Client()}
got, err := g.generate(
tt.args.ctx,
tt.args.jsonSpec,
tt.args.kube,
tt.args.namespace,
)
if (err != nil) != tt.wantErr {
t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Generator.Generate() = %s, want %s", got, tt.want)
}
})
}
}

View file

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View file

@ -22,6 +22,7 @@ import (
_ "github.com/external-secrets/external-secrets/pkg/generator/ecr"
_ "github.com/external-secrets/external-secrets/pkg/generator/fake"
_ "github.com/external-secrets/external-secrets/pkg/generator/gcr"
_ "github.com/external-secrets/external-secrets/pkg/generator/github"
_ "github.com/external-secrets/external-secrets/pkg/generator/password"
_ "github.com/external-secrets/external-secrets/pkg/generator/vault"
_ "github.com/external-secrets/external-secrets/pkg/generator/webhook"