Skip to content

Commit

Permalink
pkg/kubeapiserver/options: Improving test coverage
Browse files Browse the repository at this point in the history
Signed-off-by: TommyStarK <thomasmilox@gmail.com>
  • Loading branch information
TommyStarK committed Dec 2, 2022
1 parent 3e26e10 commit b52d485
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 8 deletions.
28 changes: 28 additions & 0 deletions pkg/kubeapiserver/options/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package options
import (
"reflect"
"testing"

"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
)

func TestValidate(t *testing.T) {
Expand Down Expand Up @@ -51,6 +54,12 @@ func TestValidate(t *testing.T) {
if errs := options.Validate(); len(errs) > 0 {
t.Errorf("Unexpected err: %v", errs)
}

// nil pointer
options = nil
if errs := options.Validate(); errs != nil {
t.Errorf("expected no errors, error found %+v", errs)
}
}

func TestComputeEnabledAdmission(t *testing.T) {
Expand Down Expand Up @@ -86,3 +95,22 @@ func TestComputeEnabledAdmission(t *testing.T) {
})
}
}

func TestAdmissionOptionsAddFlags(t *testing.T) {
var args = []string{
"--enable-admission-plugins=foo,bar,baz",
"--admission-control-config-file=admission_control_config.yaml",
}

opts := NewAdmissionOptions()
pf := pflag.NewFlagSet("test-admission-opts", pflag.ContinueOnError)
opts.AddFlags(pf)

if err := pf.Parse(args); err != nil {
t.Fatal(err)
}

// using assert because cannot compare neither pointer nor function of underlying GenericAdmission
assert.Equal(t, opts.GenericAdmission.ConfigFile, "admission_control_config.yaml")
assert.Equal(t, opts.GenericAdmission.EnablePlugins, []string{"foo", "bar", "baz"})
}
162 changes: 154 additions & 8 deletions pkg/kubeapiserver/options/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
"time"

"github.com/google/go-cmp/cmp"
"github.com/spf13/pflag"

utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
Expand All @@ -34,10 +36,11 @@ import (

func TestAuthenticationValidate(t *testing.T) {
testCases := []struct {
name string
testOIDC *OIDCAuthenticationOptions
testSA *ServiceAccountAuthenticationOptions
expectErr string
name string
testOIDC *OIDCAuthenticationOptions
testSA *ServiceAccountAuthenticationOptions
testWebHook *WebHookAuthenticationOptions
expectErr string
}{
{
name: "test when OIDC and ServiceAccounts are nil",
Expand Down Expand Up @@ -133,13 +136,70 @@ func TestAuthenticationValidate(t *testing.T) {
},
expectErr: "service-account-issuer \"http://[::1]:namedport\" contained a ':' but was not a valid URL",
},
{
name: "test when ServiceAccounts has invalid JWKSURI",
testOIDC: &OIDCAuthenticationOptions{
UsernameClaim: "sub",
SigningAlgs: []string{"RS256"},
IssuerURL: "testIssuerURL",
ClientID: "testClientID",
},
testSA: &ServiceAccountAuthenticationOptions{
KeyFiles: []string{"cert", "key"},
Issuers: []string{"http://foo.bar.com"},
JWKSURI: "https://host:port",
},
expectErr: "service-account-jwks-uri must be a valid URL: parse \"https://host:port\": invalid port \":port\" after host",
},
{
name: "test when ServiceAccounts has invalid JWKSURI (not https scheme)",
testOIDC: &OIDCAuthenticationOptions{
UsernameClaim: "sub",
SigningAlgs: []string{"RS256"},
IssuerURL: "testIssuerURL",
ClientID: "testClientID",
},
testSA: &ServiceAccountAuthenticationOptions{
KeyFiles: []string{"cert", "key"},
Issuers: []string{"http://foo.bar.com"},
JWKSURI: "http://baz.com",
},
expectErr: "service-account-jwks-uri requires https scheme, parsed as: http://baz.com",
},
{
name: "test when WebHook has invalid retry attempts",
testOIDC: &OIDCAuthenticationOptions{
UsernameClaim: "sub",
SigningAlgs: []string{"RS256"},
IssuerURL: "testIssuerURL",
ClientID: "testClientID",
},
testSA: &ServiceAccountAuthenticationOptions{
KeyFiles: []string{"cert", "key"},
Issuers: []string{"http://foo.bar.com"},
JWKSURI: "https://baz.com",
},
testWebHook: &WebHookAuthenticationOptions{
ConfigFile: "configfile",
Version: "v1",
CacheTTL: 60 * time.Second,
RetryBackoff: &wait.Backoff{
Duration: 500 * time.Millisecond,
Factor: 1.5,
Jitter: 0.2,
Steps: 0,
},
},
expectErr: "number of webhook retry attempts must be greater than 0, but is: 0",
},
}

for _, testcase := range testCases {
t.Run(testcase.name, func(t *testing.T) {
options := NewBuiltInAuthenticationOptions()
options.OIDC = testcase.testOIDC
options.ServiceAccounts = testcase.testSA
options.WebHook = testcase.testWebHook

errs := options.Validate()
if len(errs) > 0 && (!strings.Contains(utilerrors.NewAggregate(errs).Error(), testcase.expectErr) || testcase.expectErr == "") {
Expand All @@ -161,7 +221,7 @@ func TestToAuthenticationConfig(t *testing.T) {
ClientCA: "testdata/root.pem",
},
WebHook: &WebHookAuthenticationOptions{
CacheTTL: 180000000000,
CacheTTL: 1 * time.Second,
ConfigFile: "/token-webhook-config",
},
BootstrapToken: &BootstrapTokenAuthenticationOptions{
Expand Down Expand Up @@ -189,7 +249,7 @@ func TestToAuthenticationConfig(t *testing.T) {
TokenFile: "/testTokenFile",
},
TokenSuccessCacheTTL: 10 * time.Second,
TokenFailureCacheTTL: 0,
TokenFailureCacheTTL: 2 * time.Second,
}

expectConfig := kubeauthenticator.Config{
Expand All @@ -206,10 +266,10 @@ func TestToAuthenticationConfig(t *testing.T) {
ServiceAccountLookup: true,
ServiceAccountIssuers: []string{"http://foo.bar.com"},
WebhookTokenAuthnConfigFile: "/token-webhook-config",
WebhookTokenAuthnCacheTTL: 180000000000,
WebhookTokenAuthnCacheTTL: 1 * time.Second, // trigger warning webhook cache ttl shorter than overall cache ttl for successful token authentication attempts

TokenSuccessCacheTTL: 10 * time.Second,
TokenFailureCacheTTL: 0,
TokenFailureCacheTTL: 2 * time.Second, // trigger warning webhook cache ttl shorter than overall cache ttl for failed token authentication attempts

RequestHeaderConfig: &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"},
Expand Down Expand Up @@ -239,3 +299,89 @@ func TestToAuthenticationConfig(t *testing.T) {
t.Error(cmp.Diff(resultConfig, expectConfig))
}
}

func TestBuiltInAuthenticationOptionsAddFlags(t *testing.T) {
var args = []string{
"--api-audiences=foo",
"--anonymous-auth=true",
"--enable-bootstrap-token-auth=true",
"--oidc-issuer-url=https://baz.com",
"--oidc-client-id=client-id",
"--oidc-ca-file=cert",
"--oidc-username-prefix=-",
"--client-ca-file=client-cacert",
"--requestheader-client-ca-file=testdata/root.pem",
"--requestheader-username-headers=x-remote-user-custom",
"--requestheader-group-headers=x-remote-group-custom",
"--requestheader-allowed-names=kube-aggregator",
"--service-account-key-file=cert",
"--service-account-key-file=key",
"--service-account-issuer=http://foo.bar.com",
"--service-account-jwks-uri=https://qux.com",
"--token-auth-file=tokenfile",
"--authentication-token-webhook-config-file=webhook_config.yaml",
"--authentication-token-webhook-cache-ttl=180s",
}

expected := &BuiltInAuthenticationOptions{
APIAudiences: []string{"foo"},
Anonymous: &AnonymousAuthenticationOptions{
Allow: true,
},
BootstrapToken: &BootstrapTokenAuthenticationOptions{
Enable: true,
},
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
ClientCA: "client-cacert",
},
OIDC: &OIDCAuthenticationOptions{
CAFile: "cert",
ClientID: "client-id",
IssuerURL: "https://baz.com",
UsernameClaim: "sub",
UsernamePrefix: "-",
SigningAlgs: []string{"RS256"},
},
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{
ClientCAFile: "testdata/root.pem",
UsernameHeaders: []string{"x-remote-user-custom"},
GroupHeaders: []string{"x-remote-group-custom"},
AllowedNames: []string{"kube-aggregator"},
},
ServiceAccounts: &ServiceAccountAuthenticationOptions{
KeyFiles: []string{"cert", "key"},
Lookup: true,
Issuers: []string{"http://foo.bar.com"},
JWKSURI: "https://qux.com",
ExtendExpiration: true,
},
TokenFile: &TokenFileAuthenticationOptions{
TokenFile: "tokenfile",
},
WebHook: &WebHookAuthenticationOptions{
ConfigFile: "webhook_config.yaml",
Version: "v1beta1",
CacheTTL: 180 * time.Second,
RetryBackoff: &wait.Backoff{
Duration: 500 * time.Millisecond,
Factor: 1.5,
Jitter: 0.2,
Steps: 5,
},
},
TokenSuccessCacheTTL: 10 * time.Second,
TokenFailureCacheTTL: 0 * time.Second,
}

opts := NewBuiltInAuthenticationOptions().WithAll()
pf := pflag.NewFlagSet("test-builtin-authentication-opts", pflag.ContinueOnError)
opts.AddFlags(pf)

if err := pf.Parse(args); err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(opts, expected) {
t.Error(cmp.Diff(opts, expected))
}
}
64 changes: 64 additions & 0 deletions pkg/kubeapiserver/options/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ limitations under the License.
package options

import (
"fmt"
"reflect"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
)

Expand All @@ -42,6 +48,11 @@ func TestAuthzValidate(t *testing.T) {
modes: []string{},
expectErr: true,
},
{
name: "ModeAlwaysAllow specified more than once",
modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysAllow},
expectErr: true,
},
{
name: "ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile",
modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny},
Expand Down Expand Up @@ -101,4 +112,57 @@ func TestAuthzValidate(t *testing.T) {
}
})
}

// invalid number of webhook retry attempts
opts := NewBuiltInAuthorizationOptions()
opts.WebhookRetryBackoff = &wait.Backoff{
Steps: 0,
}
if errs := opts.Validate(); len(errs) == 0 {
t.Error("expected errors, no errors found")
}

// nil pointer
opts = nil
if errs := opts.Validate(); errs != nil {
t.Errorf("expected no errors, error found %+v", errs)
}
}

func TestBuiltInAuthorizationOptionsAddFlags(t *testing.T) {
var args = []string{
fmt.Sprintf("--authorization-mode=%s,%s,%s,%s", modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC, modes.ModeWebhook),
"--authorization-policy-file=policy_file.json",
"--authorization-webhook-config-file=webhook_config_file.yaml",
"--authorization-webhook-version=v1",
"--authorization-webhook-cache-authorized-ttl=60s",
"--authorization-webhook-cache-unauthorized-ttl=30s",
}

expected := &BuiltInAuthorizationOptions{
Modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC, modes.ModeWebhook},
PolicyFile: "policy_file.json",
WebhookConfigFile: "webhook_config_file.yaml",
WebhookVersion: "v1",
WebhookCacheAuthorizedTTL: 60 * time.Second,
WebhookCacheUnauthorizedTTL: 30 * time.Second,
WebhookRetryBackoff: &wait.Backoff{
Duration: 500 * time.Millisecond,
Factor: 1.5,
Jitter: 0.2,
Steps: 5,
},
}

opts := NewBuiltInAuthorizationOptions()
pf := pflag.NewFlagSet("test-builtin-authorization-opts", pflag.ContinueOnError)
opts.AddFlags(pf)

if err := pf.Parse(args); err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(opts, expected) {
t.Error(cmp.Diff(opts, expected))
}
}

0 comments on commit b52d485

Please sign in to comment.