mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
Signed-off-by: Brett Thompson <[email protected]> Co-authored-by: Jooho Lee <[email protected]> Co-authored-by: Dan Sun <[email protected]>
2359 lines
68 KiB
Go
2359 lines
68 KiB
Go
/*
|
|
Copyright 2021 The KServe Authors.
|
|
|
|
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 credentials
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/onsi/gomega/types"
|
|
|
|
"github.com/kserve/kserve/pkg/credentials/azure"
|
|
"github.com/kserve/kserve/pkg/credentials/gcs"
|
|
"github.com/kserve/kserve/pkg/credentials/hdfs"
|
|
"github.com/kserve/kserve/pkg/credentials/s3"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/onsi/gomega"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
knservingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
|
)
|
|
|
|
var configMap = &corev1.ConfigMap{
|
|
Data: map[string]string{
|
|
"credentials": `{
|
|
"storageSecretNameAnnotation": "serving.kserve.io/storageSecretName",
|
|
"storageSpecSecretName": "storage-secret",
|
|
"gcs" : {"gcsCredentialFileName": "gcloud-application-credentials.json"},
|
|
"s3" : {
|
|
"s3AccessKeyIDName": "awsAccessKeyID",
|
|
"s3SecretAccessKeyName": "awsSecretAccessKey",
|
|
"s3Endpoint": "s3.amazonaws.com",
|
|
"s3UseHttps": "1",
|
|
"s3Region": "us-east-2",
|
|
"s3VerifySSL": "1",
|
|
"s3UseVirtualBucket": "",
|
|
"s3UseAnonymousCredential": "false",
|
|
"s3CABundleConfigMap": "",
|
|
"s3CABundle": ""
|
|
}
|
|
}`,
|
|
},
|
|
}
|
|
|
|
func TestS3CredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
existingS3Secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "s3.aws.com",
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
},
|
|
}
|
|
existingServiceAccountWithProtocol := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
existingS3SecretWithProtocol := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "http://s3.aws.com",
|
|
s3.InferenceServiceS3SecretSSLAnnotation: "0",
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
secret *corev1.Secret
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build s3 secrets envs": {
|
|
serviceAccount: existingServiceAccount,
|
|
secret: existingS3Secret,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "https://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
"Build s3 secrets envs with protocol": {
|
|
serviceAccount: existingServiceAccountWithProtocol,
|
|
secret: existingS3SecretWithProtocol,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "http://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestS3CredentialBuilderWithSecretData(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
existingS3Secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
s3.S3Endpoint: []byte("s3.aws.com"),
|
|
},
|
|
}
|
|
|
|
existingServiceAccountWithProtocol := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
existingS3SecretWithProtocol := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
s3.S3Endpoint: []byte("http://s3.aws.com"),
|
|
s3.S3VerifySSL: []byte("0"),
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
secret *corev1.Secret
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build s3 secrets data envs": {
|
|
serviceAccount: existingServiceAccount,
|
|
secret: existingS3Secret,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "https://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
"Build s3 secrets data envs with protocol": {
|
|
serviceAccount: existingServiceAccountWithProtocol,
|
|
secret: existingS3SecretWithProtocol,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "http://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestS3CredentialBuilderWithStorageSecret(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
existingS3Secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "s3.aws.com",
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
},
|
|
}
|
|
existingServiceAccountWithProtocol := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
existingS3SecretWithProtocol := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "http://s3.aws.com",
|
|
s3.InferenceServiceS3SecretSSLAnnotation: "0",
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
secret *corev1.Secret
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build s3 secrets envs": {
|
|
serviceAccount: existingServiceAccount,
|
|
secret: existingS3Secret,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "https://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
"Build s3 secrets envs with protocol": {
|
|
serviceAccount: existingServiceAccountWithProtocol,
|
|
secret: existingS3SecretWithProtocol,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "http://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
annotations := map[string]string{
|
|
"serving.kserve.io/storageSecretName": scenario.secret.Name,
|
|
}
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
annotations,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestS3CredentialBuilderWithStorageSecretData(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
existingS3Secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
s3.S3Endpoint: []byte("s3.aws.com"),
|
|
},
|
|
}
|
|
|
|
existingServiceAccountWithProtocol := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
existingS3SecretWithProtocol := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "s3-secret-with-protocol",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"awsAccessKeyID": {},
|
|
"awsSecretAccessKey": {},
|
|
s3.S3Endpoint: []byte("http://s3.aws.com"),
|
|
s3.S3VerifySSL: []byte("0"),
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
secret *corev1.Secret
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build s3 secret data envs": {
|
|
serviceAccount: existingServiceAccount,
|
|
secret: existingS3Secret,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "https://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
"Build s3 secret data envs with protocol": {
|
|
serviceAccount: existingServiceAccountWithProtocol,
|
|
secret: existingS3SecretWithProtocol,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.AWSAccessKeyId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsAccessKeyID",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.AWSSecretAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "s3-secret-with-protocol",
|
|
},
|
|
Key: "awsSecretAccessKey",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "http://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "false",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
annotations := map[string]string{
|
|
"serving.kserve.io/storageSecretName": scenario.secret.Name,
|
|
}
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
annotations,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), scenario.secret)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestS3ServiceAccountCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "s3.aws.com",
|
|
s3.InferenceServiceS3UseAnonymousCredential: "true",
|
|
AwsIrsaAnnotationKey: "arn:aws:iam::123456789012:role/s3access",
|
|
},
|
|
},
|
|
}
|
|
existingServiceAccountWithProtocol := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default-with-protocol",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
s3.InferenceServiceS3SecretEndpointAnnotation: "http://s3.aws.com",
|
|
s3.InferenceServiceS3UseAnonymousCredential: "true",
|
|
AwsIrsaAnnotationKey: "arn:aws:iam::123456789012:role/s3access",
|
|
s3.InferenceServiceS3SecretSSLAnnotation: "0",
|
|
},
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build s3 service account envs": {
|
|
serviceAccount: existingServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "https://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "1",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "true",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
"Build s3 service account envs with protocol": {
|
|
serviceAccount: existingServiceAccountWithProtocol,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: s3.S3UseHttps,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.S3Endpoint,
|
|
Value: "s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.AWSEndpointUrl,
|
|
Value: "http://s3.aws.com",
|
|
},
|
|
{
|
|
Name: s3.S3VerifySSL,
|
|
Value: "0",
|
|
},
|
|
{
|
|
Name: s3.AWSAnonymousCredential,
|
|
Value: "true",
|
|
},
|
|
{
|
|
Name: s3.AWSRegion,
|
|
Value: "us-east-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), scenario.serviceAccount)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestGCSCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
existingServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "user-gcp-sa",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
existingGCSSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "user-gcp-sa",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"gcloud-application-credentials.json": {},
|
|
},
|
|
}
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Build gcs secrets volume": {
|
|
serviceAccount: existingServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
VolumeMounts: []corev1.VolumeMount{
|
|
{
|
|
Name: gcs.GCSCredentialVolumeName,
|
|
ReadOnly: true,
|
|
MountPath: gcs.GCSCredentialVolumeMountPath,
|
|
},
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: gcs.GCSCredentialEnvKey,
|
|
Value: gcs.GCSCredentialVolumeMountPath + "gcloud-application-credentials.json",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []corev1.Volume{
|
|
{
|
|
Name: gcs.GCSCredentialVolumeName,
|
|
VolumeSource: corev1.VolumeSource{
|
|
Secret: &corev1.SecretVolumeSource{
|
|
SecretName: "user-gcp-sa",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
g.Expect(c.Create(t.Context(), existingServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), existingGCSSecret)).NotTo(gomega.HaveOccurred())
|
|
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
g.Expect(c.Delete(t.Context(), existingServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), existingGCSSecret)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func TestLegacyAzureCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
customOnlyServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "custom-sa",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
customAzureSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"AZ_SUBSCRIPTION_ID": {},
|
|
"AZ_TENANT_ID": {},
|
|
"AZ_CLIENT_ID": {},
|
|
"AZ_CLIENT_SECRET": {},
|
|
},
|
|
}
|
|
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Custom Azure Secret": {
|
|
serviceAccount: customOnlyServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: azure.AzureSubscriptionId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.LegacyAzureSubscriptionId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureTenantId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.LegacyAzureTenantId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureClientId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.LegacyAzureClientId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureClientSecret,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.LegacyAzureClientSecret,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
g.Expect(c.Create(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Expect(c.Delete(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
|
|
func TestHdfsCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
customOnlyServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "custom-sa",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "hdfs-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
customHdfsSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "hdfs-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
hdfs.HdfsNamenode: []byte("https://testdomain:port"),
|
|
hdfs.HdfsRootPath: []byte("/"),
|
|
hdfs.KerberosPrincipal: []byte("account@REALM"),
|
|
hdfs.KerberosKeytab: []byte("AAA="),
|
|
},
|
|
}
|
|
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Custom HDFS Secret": {
|
|
serviceAccount: customOnlyServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
VolumeMounts: []corev1.VolumeMount{
|
|
{
|
|
Name: hdfs.HdfsVolumeName,
|
|
ReadOnly: true,
|
|
MountPath: hdfs.MountPath,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []corev1.Volume{
|
|
{
|
|
Name: hdfs.HdfsVolumeName,
|
|
VolumeSource: corev1.VolumeSource{
|
|
Secret: &corev1.SecretVolumeSource{
|
|
SecretName: "hdfs-custom-secret",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
g.Expect(c.Create(t.Context(), customHdfsSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Expect(c.Delete(t.Context(), customHdfsSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
|
|
func TestAzureCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
customOnlyServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "custom-sa",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
customAzureSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"AZURE_SUBSCRIPTION_ID": {},
|
|
"AZURE_TENANT_ID": {},
|
|
"AZURE_CLIENT_ID": {},
|
|
"AZURE_CLIENT_SECRET": {},
|
|
"AZURE_STORAGE_ACCESS_KEY": {},
|
|
},
|
|
}
|
|
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Custom Azure Secret": {
|
|
serviceAccount: customOnlyServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: azure.AzureSubscriptionId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureSubscriptionId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureTenantId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureTenantId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureClientId,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureClientId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureClientSecret,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureClientSecret,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: azure.AzureStorageAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureStorageAccessKey,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
g.Expect(c.Create(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Expect(c.Delete(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
|
|
func TestAzureStorageAccessKeyCredentialBuilder(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
customOnlyServiceAccount := &corev1.ServiceAccount{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "custom-sa",
|
|
Namespace: "default",
|
|
},
|
|
Secrets: []corev1.ObjectReference{
|
|
{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
customAzureSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "az-custom-secret",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"AZURE_STORAGE_ACCESS_KEY": {},
|
|
},
|
|
}
|
|
|
|
scenarios := map[string]struct {
|
|
serviceAccount *corev1.ServiceAccount
|
|
inputConfiguration *knservingv1.Configuration
|
|
expectedConfiguration *knservingv1.Configuration
|
|
shouldFail bool
|
|
}{
|
|
"Custom Azure Secret": {
|
|
serviceAccount: customOnlyServiceAccount,
|
|
inputConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedConfiguration: &knservingv1.Configuration{
|
|
Spec: knservingv1.ConfigurationSpec{
|
|
Template: knservingv1.RevisionTemplateSpec{
|
|
Spec: knservingv1.RevisionSpec{
|
|
PodSpec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: azure.AzureStorageAccessKey,
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "az-custom-secret",
|
|
},
|
|
Key: azure.AzureStorageAccessKey,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
},
|
|
}
|
|
|
|
g.Expect(c.Create(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Create(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
for name, scenario := range scenarios {
|
|
err := builder.CreateSecretVolumeAndEnv(
|
|
t.Context(),
|
|
scenario.serviceAccount.Namespace,
|
|
nil,
|
|
scenario.serviceAccount.Name,
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Containers[0],
|
|
&scenario.inputConfiguration.Spec.Template.Spec.Volumes,
|
|
)
|
|
if scenario.shouldFail && err == nil {
|
|
t.Errorf("Test %q failed: returned success but expected error", name)
|
|
}
|
|
// Validate
|
|
if !scenario.shouldFail {
|
|
if err != nil {
|
|
t.Errorf("Test %q failed: returned error: %v", name, err)
|
|
}
|
|
if diff := cmp.Diff(scenario.expectedConfiguration, scenario.inputConfiguration); diff != "" {
|
|
t.Errorf("Test %q unexpected configuration spec (-want +got): %v", name, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Expect(c.Delete(t.Context(), customAzureSecret)).NotTo(gomega.HaveOccurred())
|
|
g.Expect(c.Delete(t.Context(), customOnlyServiceAccount)).NotTo(gomega.HaveOccurred())
|
|
}
|
|
|
|
func TestCredentialBuilder_CreateStorageSpecSecretEnvs(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
namespace := "default"
|
|
builder := NewCredentialBuilder(c, clientset, configMap)
|
|
|
|
scenarios := map[string]struct {
|
|
secret *corev1.Secret
|
|
storageKey string
|
|
storageSecretName string
|
|
overrideParams map[string]string
|
|
container *corev1.Container
|
|
shouldFail bool
|
|
matcher types.GomegaMatcher
|
|
}{
|
|
"fail on storage secret name is empty": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-secret",
|
|
Namespace: namespace,
|
|
},
|
|
Data: nil,
|
|
},
|
|
storageKey: "",
|
|
storageSecretName: "",
|
|
overrideParams: make(map[string]string),
|
|
container: &corev1.Container{},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
"storage spec with empty override params": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "", "bucket": ""},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "minio",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"\",\"type\":\"\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"simple storage spec": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "minio",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"test-bucket\",\"type\":\"s3\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"wrong storage key": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "wrong-key",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
"default storage key": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{DefaultStorageSecretKey + "_s3": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "default_s3",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"test-bucket\",\"type\":\"s3\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"default storage key with empty storage type": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{DefaultStorageSecretKey: "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "default",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"test-bucket\",\"type\":\"\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"storage spec with uri scheme placeholder": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "s3", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"<scheme-placeholder>://models/example-model/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/example-model/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "minio",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"test-bucket\",\"type\":\"s3\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"hdfs with uri scheme placeholder": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"hdfs": "{\n \"type\": \"hdfs\",\n \"access_key_id\": \"hdfs34\",\n \"secret_access_key\": \"hdfs123\",\n \"endpoint_url\": \"http://hdfs-service.kubeflow\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "hdfs",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "hdfs", "bucket": ""},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"<scheme-placeholder>://models/example-model/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: false,
|
|
matcher: gomega.Equal(&corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"hdfs://models/example-model/",
|
|
"/mnt/models/",
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "STORAGE_CONFIG",
|
|
Value: "",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: nil,
|
|
ResourceFieldRef: nil,
|
|
ConfigMapKeyRef: nil,
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "storage-secret",
|
|
},
|
|
Key: "hdfs",
|
|
Optional: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "STORAGE_OVERRIDE_CONFIG",
|
|
Value: "{\"bucket\":\"\",\"type\":\"hdfs\"}",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
"unsupported storage type": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"gs\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"gs://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
"secret data with syntax error": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n { \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
"fail on storage type is empty": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"test-bucket\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "", "bucket": "test-bucket"},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"s3://test-bucket/models/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
"fail on bucket is empty on s3 storage": {
|
|
secret: &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "storage-secret",
|
|
Namespace: namespace,
|
|
},
|
|
StringData: map[string]string{"minio": "{\n \"type\": \"s3\",\n \"access_key_id\": \"minio\",\n \"secret_access_key\": \"minio123\",\n \"endpoint_url\": \"http://minio-service.kubeflow:9000\",\n \"bucket\": \"\",\n \"region\": \"us-south\"\n }"},
|
|
},
|
|
storageKey: "minio",
|
|
storageSecretName: "storage-secret",
|
|
overrideParams: map[string]string{"type": "s3", "bucket": ""},
|
|
container: &corev1.Container{
|
|
Name: "init-container",
|
|
Image: "kserve/init-container:latest",
|
|
Args: []string{
|
|
"<scheme-placeholder>://models/example-model/",
|
|
"/mnt/models/",
|
|
},
|
|
},
|
|
shouldFail: true,
|
|
matcher: gomega.HaveOccurred(),
|
|
},
|
|
}
|
|
|
|
for _, tc := range scenarios {
|
|
if err := c.Create(t.Context(), tc.secret); err != nil {
|
|
t.Errorf("Failed to create secret %s: %v", "storage-secret", err)
|
|
}
|
|
err := builder.CreateStorageSpecSecretEnvs(t.Context(), namespace, nil, tc.storageKey, tc.overrideParams, tc.container)
|
|
if !tc.shouldFail {
|
|
g.Expect(err).ShouldNot(gomega.HaveOccurred())
|
|
g.Expect(tc.container).Should(tc.matcher)
|
|
} else {
|
|
g.Expect(err).To(tc.matcher)
|
|
}
|
|
if err := c.Delete(t.Context(), tc.secret); err != nil {
|
|
t.Errorf("Failed to delete secret %s because of: %v", tc.secret.Name, err)
|
|
}
|
|
}
|
|
}
|