Merge pull request #1072 from ericchiang/k8s-test
*: run kubernetes tests in travis
This commit is contained in:
		| @@ -8,6 +8,7 @@ go: | ||||
|  | ||||
| services: | ||||
|   - postgresql | ||||
|   - docker | ||||
|  | ||||
| env: | ||||
|   - DEX_POSTGRES_DATABASE=postgres DEX_POSTGRES_USER=postgres DEX_POSTGRES_HOST="localhost" DEX_LDAP_TESTS=1 DEBIAN_FRONTEND=noninteractive | ||||
| @@ -21,6 +22,7 @@ install: | ||||
|  | ||||
| script: | ||||
|   - make testall | ||||
|   - ./scripts/test-k8s.sh | ||||
|  | ||||
| notifications: | ||||
|   email: false | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| ## Kubernetes | ||||
|  | ||||
| Kubernetes tests will only run if the `DEX_KUBECONFIG` environment variable is set. | ||||
| Kubernetes tests run against a Kubernetes API server, and are enabled by the `DEX_KUBECONFIG` environment variable: | ||||
|  | ||||
| ``` | ||||
| $ export DEX_KUBECONFIG=~/.kube/config | ||||
| @@ -10,7 +10,11 @@ $ go test -v -i ./storage/kubernetes | ||||
| $ go test -v ./storage/kubernetes | ||||
| ``` | ||||
|  | ||||
| Because third party resources creation isn't synchronized it's expected that the tests fail the first time. Fear not, and just run them again. | ||||
| These tests can be executed locally using docker by running the following script: | ||||
|  | ||||
| ``` | ||||
| $ ./scripts/test-k8s.sh | ||||
| ``` | ||||
|  | ||||
| ## Postgres | ||||
|  | ||||
|   | ||||
							
								
								
									
										56
									
								
								scripts/test-k8s.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										56
									
								
								scripts/test-k8s.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| TEMPDIR=$( mktemp -d ) | ||||
|  | ||||
| cat << EOF > $TEMPDIR/kubeconfig | ||||
| apiVersion: v1 | ||||
| kind: Config | ||||
| clusters: | ||||
| - name: local | ||||
|   cluster: | ||||
|     server: http://localhost:8080 | ||||
| users: | ||||
| - name: local | ||||
|   user: | ||||
| contexts: | ||||
| - context: | ||||
|     cluster: local | ||||
|     user: local | ||||
| EOF | ||||
|  | ||||
| cleanup () { | ||||
|     docker rm -f $( cat $TEMPDIR/etcd ) | ||||
|     docker rm -f $( cat $TEMPDIR/kube-apiserver ) | ||||
|     rm -rf $TEMPDIR | ||||
| } | ||||
|  | ||||
| trap "{ CODE=$?; cleanup ; exit $CODE; }" EXIT | ||||
|  | ||||
| docker run \ | ||||
|     --cidfile=$TEMPDIR/etcd \ | ||||
|     -d \ | ||||
|     --net=host \ | ||||
|     gcr.io/google_containers/etcd:3.1.10 \ | ||||
|     etcd | ||||
|  | ||||
| docker run \ | ||||
|     --cidfile=$TEMPDIR/kube-apiserver \ | ||||
|     -d \ | ||||
|     -v $TEMPDIR:/var/run/kube-test:ro \ | ||||
|     --net=host \ | ||||
|     gcr.io/google_containers/kube-apiserver-amd64:v1.7.4 \ | ||||
|     kube-apiserver \ | ||||
|     --etcd-servers=http://localhost:2379 \ | ||||
|     --service-cluster-ip-range=10.0.0.1/16 \ | ||||
|     --insecure-bind-address=0.0.0.0 \ | ||||
|     --insecure-port=8080 | ||||
|  | ||||
| until $(curl --output /dev/null --silent --head --fail http://localhost:8080/healthz); do | ||||
|     printf '.' | ||||
|     sleep 1 | ||||
| done | ||||
| echo "API server ready" | ||||
|  | ||||
| export DEX_KUBECONFIG=$TEMPDIR/kubeconfig | ||||
| go test -v -i ./storage/kubernetes | ||||
| go test -v ./storage/kubernetes | ||||
| @@ -157,7 +157,11 @@ func closeResp(r *http.Response) { | ||||
| } | ||||
|  | ||||
| func (c *client) get(resource, name string, v interface{}) error { | ||||
| 	url := c.urlFor(c.apiVersion, c.namespace, resource, name) | ||||
| 	return c.getResource(c.apiVersion, c.namespace, resource, name, v) | ||||
| } | ||||
|  | ||||
| func (c *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error { | ||||
| 	url := c.urlFor(apiVersion, namespace, resource, name) | ||||
| 	resp, err := c.client.Get(url) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -53,9 +53,9 @@ func (c *Config) Open(logger logrus.FieldLogger) (storage.Storage, error) { | ||||
| // open returns a kubernetes client, initializing the third party resources used | ||||
| // by dex. | ||||
| // | ||||
| // errOnResources controls if errors creating the resources cause this method to return | ||||
| // waitForResources controls if errors creating the resources cause this method to return | ||||
| // immediately (used during testing), or if the client will asynchronously retry. | ||||
| func (c *Config) open(logger logrus.FieldLogger, errOnResources bool) (*client, error) { | ||||
| func (c *Config) open(logger logrus.FieldLogger, waitForResources bool) (*client, error) { | ||||
| 	if c.InCluster && (c.KubeConfigFile != "") { | ||||
| 		return nil, errors.New("cannot specify both 'inCluster' and 'kubeConfigFile'") | ||||
| 	} | ||||
| @@ -87,7 +87,7 @@ func (c *Config) open(logger logrus.FieldLogger, errOnResources bool) (*client, | ||||
|  | ||||
| 	logger.Info("creating custom Kubernetes resources") | ||||
| 	if !cli.registerCustomResources(c.UseTPR) { | ||||
| 		if errOnResources { | ||||
| 		if waitForResources { | ||||
| 			cancel() | ||||
| 			return nil, fmt.Errorf("failed creating custom resources") | ||||
| 		} | ||||
| @@ -111,6 +111,13 @@ func (c *Config) open(logger logrus.FieldLogger, errOnResources bool) (*client, | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	if waitForResources { | ||||
| 		if err := cli.waitForCRDs(ctx); err != nil { | ||||
| 			cancel() | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If the client is closed, stop trying to create resources. | ||||
| 	cli.cancel = cancel | ||||
| 	return cli, nil | ||||
| @@ -123,9 +130,6 @@ func (c *Config) open(logger logrus.FieldLogger, errOnResources bool) (*client, | ||||
| // It logs all errors, returning true if the resources were created successfully. | ||||
| // | ||||
| // Creating a custom resource does not mean that they'll be immediately available. | ||||
| // | ||||
| // TODO(ericchiang): Provide an option to wait for the resources to actually | ||||
| // be available. | ||||
| func (cli *client) registerCustomResources(useTPR bool) (ok bool) { | ||||
| 	ok = true | ||||
| 	length := len(customResourceDefinitions) | ||||
| @@ -165,6 +169,49 @@ func (cli *client) registerCustomResources(useTPR bool) (ok bool) { | ||||
| 	return ok | ||||
| } | ||||
|  | ||||
| // waitForCRDs waits for all CRDs to be in a ready state, and is used | ||||
| // by the tests to synchronize before running conformance. | ||||
| func (cli *client) waitForCRDs(ctx context.Context) error { | ||||
| 	ctx, cancel := context.WithTimeout(ctx, time.Second*30) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	for _, crd := range customResourceDefinitions { | ||||
| 		for { | ||||
| 			err := cli.isCRDReady(crd.Name) | ||||
| 			if err == nil { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			cli.logger.Errorf("checking CRD: %v", err) | ||||
|  | ||||
| 			select { | ||||
| 			case <-ctx.Done(): | ||||
| 				return errors.New("timed out waiting for CRDs to be available") | ||||
| 			case <-time.After(time.Millisecond * 100): | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // isCRDReady determines if a CRD is ready by inspecting its conditions. | ||||
| func (cli *client) isCRDReady(name string) error { | ||||
| 	var r k8sapi.CustomResourceDefinition | ||||
| 	err := cli.getResource("apiextensions.k8s.io/v1beta1", "", "customresourcedefinitions", name, &r) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("get crd %s: %v", name, err) | ||||
| 	} | ||||
|  | ||||
| 	conds := make(map[string]string) // For debugging, keep the conditions around. | ||||
| 	for _, c := range r.Status.Conditions { | ||||
| 		if c.Type == k8sapi.Established && c.Status == k8sapi.ConditionTrue { | ||||
| 			return nil | ||||
| 		} | ||||
| 		conds[string(c.Type)] = string(c.Status) | ||||
| 	} | ||||
| 	return fmt.Errorf("crd %s not ready %#v", name, conds) | ||||
| } | ||||
|  | ||||
| func (cli *client) Close() error { | ||||
| 	if cli.cancel != nil { | ||||
| 		cli.cancel() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user