2022-03-24 22:46:10 +00:00
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
2020-10-15 19:40:56 +00:00
// SPDX-License-Identifier: Apache-2.0
package supervisor
import (
2022-04-16 02:43:53 +00:00
"context"
2022-03-24 22:46:10 +00:00
"fmt"
2020-10-15 19:40:56 +00:00
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/require"
2022-03-24 22:46:10 +00:00
"k8s.io/utils/pointer"
2020-10-15 19:40:56 +00:00
"go.pinniped.dev/internal/here"
2022-04-16 02:43:53 +00:00
"go.pinniped.dev/internal/plog"
2020-10-15 19:40:56 +00:00
)
func TestFromPath ( t * testing . T ) {
tests := [ ] struct {
name string
yaml string
wantConfig * Config
2020-10-28 18:56:50 +00:00
wantError string
2020-10-15 19:40:56 +00:00
} {
{
name : "Happy" ,
yaml : here . Doc ( `
-- -
2021-01-19 15:52:12 +00:00
apiGroupSuffix : some . suffix . com
2020-10-15 19:40:56 +00:00
labels :
myLabelKey1 : myLabelValue1
myLabelKey2 : myLabelValue2
2020-10-28 18:56:50 +00:00
names :
defaultTLSCertificateSecret : my - secret - name
2021-12-15 20:48:55 +00:00
endpoints :
https :
network : unix
address : : 1234
http :
2022-03-24 22:46:10 +00:00
network : tcp
address : 127.0 .0 .1 : 1234
2022-03-31 23:23:45 +00:00
insecureAcceptExternalUnencryptedHttpRequests : false
2022-04-16 02:43:53 +00:00
logLevel : trace
2020-10-15 19:40:56 +00:00
` ) ,
wantConfig : & Config {
2021-05-12 20:20:00 +00:00
APIGroupSuffix : pointer . StringPtr ( "some.suffix.com" ) ,
2020-10-15 19:40:56 +00:00
Labels : map [ string ] string {
"myLabelKey1" : "myLabelValue1" ,
"myLabelKey2" : "myLabelValue2" ,
} ,
2020-10-28 18:56:50 +00:00
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
2021-12-15 20:48:55 +00:00
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "unix" ,
Address : ":1234" ,
} ,
HTTP : & Endpoint {
2022-03-24 22:46:10 +00:00
Network : "tcp" ,
Address : "127.0.0.1:1234" ,
2021-12-15 20:48:55 +00:00
} ,
} ,
2022-03-31 23:23:45 +00:00
AllowExternalHTTP : false ,
2022-04-16 02:43:53 +00:00
LogLevel : func ( level plog . LogLevel ) * plog . LogLevel { return & level } ( plog . LevelTrace ) ,
Log : plog . LogSpec {
Level : plog . LevelTrace ,
} ,
} ,
} ,
{
name : "Happy with new log field" ,
yaml : here . Doc ( `
-- -
apiGroupSuffix : some . suffix . com
labels :
myLabelKey1 : myLabelValue1
myLabelKey2 : myLabelValue2
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : unix
address : : 1234
http :
network : tcp
address : 127.0 .0 .1 : 1234
insecureAcceptExternalUnencryptedHttpRequests : false
log :
level : info
format : text
` ) ,
wantConfig : & Config {
APIGroupSuffix : pointer . StringPtr ( "some.suffix.com" ) ,
Labels : map [ string ] string {
"myLabelKey1" : "myLabelValue1" ,
"myLabelKey2" : "myLabelValue2" ,
} ,
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "unix" ,
Address : ":1234" ,
} ,
HTTP : & Endpoint {
Network : "tcp" ,
Address : "127.0.0.1:1234" ,
} ,
} ,
AllowExternalHTTP : false ,
Log : plog . LogSpec {
Level : plog . LevelInfo ,
Format : plog . FormatText ,
} ,
} ,
} ,
{
name : "Happy with old and new log field" ,
yaml : here . Doc ( `
-- -
apiGroupSuffix : some . suffix . com
labels :
myLabelKey1 : myLabelValue1
myLabelKey2 : myLabelValue2
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : unix
address : : 1234
http :
network : tcp
address : 127.0 .0 .1 : 1234
insecureAcceptExternalUnencryptedHttpRequests : false
logLevel : trace
log :
level : info
format : text
` ) ,
wantConfig : & Config {
APIGroupSuffix : pointer . StringPtr ( "some.suffix.com" ) ,
Labels : map [ string ] string {
"myLabelKey1" : "myLabelValue1" ,
"myLabelKey2" : "myLabelValue2" ,
} ,
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "unix" ,
Address : ":1234" ,
} ,
HTTP : & Endpoint {
Network : "tcp" ,
Address : "127.0.0.1:1234" ,
} ,
} ,
AllowExternalHTTP : false ,
LogLevel : func ( level plog . LogLevel ) * plog . LogLevel { return & level } ( plog . LevelTrace ) ,
Log : plog . LogSpec {
Level : plog . LevelTrace ,
Format : plog . FormatText ,
} ,
2020-10-15 19:40:56 +00:00
} ,
} ,
2022-04-16 02:43:53 +00:00
{
name : "bad log format" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
log :
level : info
format : cli
` ) ,
wantError : "decode yaml: error unmarshaling JSON: while decoding JSON: invalid log format, valid choices are the empty string, json and text" ,
} ,
2020-10-15 19:40:56 +00:00
{
name : "When only the required fields are present, causes other fields to be defaulted" ,
yaml : here . Doc ( `
-- -
2020-10-28 18:56:50 +00:00
names :
defaultTLSCertificateSecret : my - secret - name
2020-10-15 19:40:56 +00:00
` ) ,
wantConfig : & Config {
2021-05-12 20:20:00 +00:00
APIGroupSuffix : pointer . StringPtr ( "pinniped.dev" ) ,
2021-01-19 15:52:12 +00:00
Labels : map [ string ] string { } ,
2020-10-28 18:56:50 +00:00
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
2021-12-15 20:48:55 +00:00
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "tcp" ,
Address : ":8443" ,
} ,
HTTP : & Endpoint {
2022-03-24 22:46:10 +00:00
Network : "disabled" ,
2021-12-15 20:48:55 +00:00
} ,
} ,
2022-03-31 23:23:45 +00:00
AllowExternalHTTP : false ,
2020-10-15 19:40:56 +00:00
} ,
} ,
2021-12-15 20:48:55 +00:00
{
name : "all endpoints disabled" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
http :
network : disabled
` ) ,
wantError : "validate endpoints: all endpoints are disabled" ,
} ,
{
name : "invalid https endpoint" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : foo
http :
network : disabled
` ) ,
wantError : ` validate https endpoint: unknown network "foo" ` ,
} ,
{
name : "invalid http endpoint" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
http :
network : bar
` ) ,
wantError : ` validate http endpoint: unknown network "bar" ` ,
} ,
2022-03-24 22:46:10 +00:00
{
2022-03-31 23:23:45 +00:00
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests missing" ,
2022-03-24 22:46:10 +00:00
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
http :
network : tcp
address : : 8080
` ) ,
wantError : ` validate http endpoint: http listener address ":8080" for "tcp" network may only bind to loopback interfaces ` ,
} ,
2022-03-29 00:03:23 +00:00
{
2022-03-31 23:23:45 +00:00
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests set to boolean false" ,
2022-03-29 00:03:23 +00:00
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
http :
network : tcp
address : : 8080
2022-03-31 23:23:45 +00:00
insecureAcceptExternalUnencryptedHttpRequests : false
2022-03-29 00:03:23 +00:00
` ) ,
wantError : ` validate http endpoint: http listener address ":8080" for "tcp" network may only bind to loopback interfaces ` ,
} ,
{
2022-03-31 23:23:45 +00:00
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests set to unsupported value" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
insecureAcceptExternalUnencryptedHttpRequests : "garbage" # this will be treated as the default , which is false
` ) ,
wantError : ` decode yaml: error unmarshaling JSON: while decoding JSON: invalid value for boolean ` ,
} ,
{
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests set to string false" ,
2022-03-29 00:03:23 +00:00
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
http :
network : tcp
address : : 8080
2022-03-31 23:23:45 +00:00
insecureAcceptExternalUnencryptedHttpRequests : "false"
2022-03-29 00:03:23 +00:00
` ) ,
wantError : ` validate http endpoint: http listener address ":8080" for "tcp" network may only bind to loopback interfaces ` ,
} ,
{
2022-03-31 23:23:45 +00:00
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests set to boolean true" ,
2022-03-29 00:03:23 +00:00
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
http :
network : tcp
address : : 1234
2022-03-31 23:23:45 +00:00
insecureAcceptExternalUnencryptedHttpRequests : true
2022-03-29 00:03:23 +00:00
` ) ,
wantConfig : & Config {
APIGroupSuffix : pointer . StringPtr ( "pinniped.dev" ) ,
Labels : map [ string ] string { } ,
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "tcp" ,
Address : ":8443" ,
} ,
HTTP : & Endpoint {
Network : "tcp" ,
Address : ":1234" ,
} ,
} ,
2022-03-31 23:23:45 +00:00
AllowExternalHTTP : true ,
2022-03-29 00:03:23 +00:00
} ,
} ,
{
2022-03-31 23:23:45 +00:00
name : "http endpoint uses tcp but binds to more than only loopback interfaces with insecureAcceptExternalUnencryptedHttpRequests set to string true" ,
2022-03-29 00:03:23 +00:00
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
http :
network : tcp
address : : 1234
2022-03-31 23:23:45 +00:00
insecureAcceptExternalUnencryptedHttpRequests : "true"
2022-03-29 00:03:23 +00:00
` ) ,
wantConfig : & Config {
APIGroupSuffix : pointer . StringPtr ( "pinniped.dev" ) ,
Labels : map [ string ] string { } ,
NamesConfig : NamesConfigSpec {
DefaultTLSCertificateSecret : "my-secret-name" ,
} ,
Endpoints : & Endpoints {
HTTPS : & Endpoint {
Network : "tcp" ,
Address : ":8443" ,
} ,
HTTP : & Endpoint {
Network : "tcp" ,
Address : ":1234" ,
} ,
} ,
2022-03-31 23:23:45 +00:00
AllowExternalHTTP : true ,
2022-03-29 00:03:23 +00:00
} ,
} ,
2021-12-15 20:48:55 +00:00
{
name : "endpoint disabled with non-empty address" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : disabled
address : wee
` ) ,
wantError : ` validate https endpoint: address set to "wee" when disabled, should be empty ` ,
} ,
{
name : "endpoint tcp with empty address" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
http :
network : tcp
` ) ,
wantError : ` validate http endpoint: address must be set with "tcp" network ` ,
} ,
{
name : "endpoint unix with empty address" ,
yaml : here . Doc ( `
-- -
names :
defaultTLSCertificateSecret : my - secret - name
endpoints :
https :
network : unix
` ) ,
wantError : ` validate https endpoint: address must be set with "unix" network ` ,
} ,
2020-10-28 18:56:50 +00:00
{
name : "Missing defaultTLSCertificateSecret name" ,
yaml : here . Doc ( `
-- -
` ) ,
wantError : "validate names: missing required names: defaultTLSCertificateSecret" ,
} ,
2021-01-13 01:27:41 +00:00
{
name : "apiGroupSuffix is prefixed with '.'" ,
yaml : here . Doc ( `
-- -
apiGroupSuffix : . starts . with . dot
names :
defaultTLSCertificateSecret : my - secret - name
` ) ,
2021-02-05 17:56:05 +00:00
wantError : "validate apiGroupSuffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')" ,
2021-01-13 01:27:41 +00:00
} ,
2020-10-15 19:40:56 +00:00
}
for _ , test := range tests {
test := test
t . Run ( test . name , func ( t * testing . T ) {
2022-04-16 02:43:53 +00:00
// this is a serial test because it sets the global logger
2022-03-31 23:23:45 +00:00
2020-10-15 19:40:56 +00:00
// Write yaml to temp file
f , err := ioutil . TempFile ( "" , "pinniped-test-config-yaml-*" )
require . NoError ( t , err )
defer func ( ) {
err := os . Remove ( f . Name ( ) )
require . NoError ( t , err )
} ( )
_ , err = f . WriteString ( test . yaml )
require . NoError ( t , err )
err = f . Close ( )
require . NoError ( t , err )
// Test FromPath()
2022-04-16 02:43:53 +00:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
t . Cleanup ( cancel )
config , err := FromPath ( ctx , f . Name ( ) )
2020-10-15 19:40:56 +00:00
2020-10-28 18:56:50 +00:00
if test . wantError != "" {
require . EqualError ( t , err , test . wantError )
} else {
require . NoError ( t , err )
require . Equal ( t , test . wantConfig , config )
}
2020-10-15 19:40:56 +00:00
} )
}
}
2022-03-24 22:46:10 +00:00
func TestAddrIsOnlyOnLoopback ( t * testing . T ) {
tests := [ ] struct {
addr string
want bool
} {
{ addr : "localhost:" , want : true } ,
{ addr : "localhost:0" , want : true } ,
{ addr : "localhost:80" , want : true } ,
{ addr : "localhost:http" , want : true } ,
2022-03-29 00:03:23 +00:00
{ addr : "ip6-localhost:" , want : true } ,
{ addr : "ip6-localhost:0" , want : true } ,
{ addr : "ip6-localhost:80" , want : true } ,
{ addr : "ip6-localhost:http" , want : true } ,
{ addr : "ip6-loopback:" , want : true } ,
{ addr : "ip6-loopback:0" , want : true } ,
{ addr : "ip6-loopback:80" , want : true } ,
{ addr : "ip6-loopback:http" , want : true } ,
2022-03-24 22:46:10 +00:00
{ addr : "127.0.0.1:" , want : true } ,
{ addr : "127.0.0.1:0" , want : true } ,
{ addr : "127.0.0.1:80" , want : true } ,
{ addr : "127.0.0.1:http" , want : true } ,
{ addr : "[::1]:" , want : true } ,
{ addr : "[::1]:0" , want : true } ,
{ addr : "[::1]:80" , want : true } ,
{ addr : "[::1]:http" , want : true } ,
{ addr : "[0:0:0:0:0:0:0:1]:" , want : true } ,
{ addr : "[0:0:0:0:0:0:0:1]:0" , want : true } ,
{ addr : "[0:0:0:0:0:0:0:1]:80" , want : true } ,
{ addr : "[0:0:0:0:0:0:0:1]:http" , want : true } ,
{ addr : "" , want : false } , // illegal input, can't be empty
{ addr : "host" , want : false } , // illegal input, need colon
{ addr : "localhost" , want : false } , // illegal input, need colon
{ addr : "127.0.0.1" , want : false } , // illegal input, need colon
{ addr : ":" , want : false } , // illegal input, need either host or port
{ addr : "2001:db8::1:80" , want : false } , // illegal input, forgot square brackets
{ addr : ":0" , want : false } ,
{ addr : ":80" , want : false } ,
{ addr : ":http" , want : false } ,
{ addr : "notlocalhost:" , want : false } ,
{ addr : "notlocalhost:0" , want : false } ,
{ addr : "notlocalhost:80" , want : false } ,
{ addr : "notlocalhost:http" , want : false } ,
{ addr : "0.0.0.0:" , want : false } ,
{ addr : "0.0.0.0:0" , want : false } ,
{ addr : "0.0.0.0:80" , want : false } ,
{ addr : "0.0.0.0:http" , want : false } ,
{ addr : "[::]:" , want : false } ,
{ addr : "[::]:0" , want : false } ,
{ addr : "[::]:80" , want : false } ,
{ addr : "[::]:http" , want : false } ,
{ addr : "42.42.42.42:" , want : false } ,
{ addr : "42.42.42.42:0" , want : false } ,
{ addr : "42.42.42.42:80" , want : false } ,
{ addr : "42.42.42.42:http" , want : false } ,
{ addr : "[2001:db8::1]:" , want : false } ,
{ addr : "[2001:db8::1]:0" , want : false } ,
{ addr : "[2001:db8::1]:80" , want : false } ,
{ addr : "[2001:db8::1]:http" , want : false } ,
{ addr : "[fe80::1%zone]:" , want : false } ,
{ addr : "[fe80::1%zone]:0" , want : false } ,
{ addr : "[fe80::1%zone]:80" , want : false } ,
{ addr : "[fe80::1%zone]:http" , want : false } ,
}
for _ , test := range tests {
tt := test
t . Run ( fmt . Sprintf ( "address %s should be %t" , tt . addr , tt . want ) , func ( t * testing . T ) {
2022-03-31 23:23:45 +00:00
t . Parallel ( )
2022-03-24 22:46:10 +00:00
require . Equal ( t , tt . want , addrIsOnlyOnLoopback ( tt . addr ) )
} )
}
}