Skip to content

Commit 1820c33

Browse files
authored
Merge pull request #61 from akshaymankar/integration-tests
Add Integration tests
2 parents 779d619 + ecff4d3 commit 1820c33

File tree

12 files changed

+461
-78
lines changed

12 files changed

+461
-78
lines changed

.travis.yml

Lines changed: 55 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@
1111
# Do not choose a language; we provide our own build tools.
1212
language: generic
1313

14+
services:
15+
- docker
16+
1417
# Caching so the next build will be fast too.
1518
cache:
1619
directories:
1720
- $HOME/.ghc
1821
- $HOME/.cabal
1922
- $HOME/.stack
2023
- $TRAVIS_BUILD_DIR/.stack-work
24+
- $TRAVIS_BUILD_DIR/examples/.stack-work
2125

2226
# The different configurations we want to test. We have BUILD=cabal which uses
2327
# cabal-install, and BUILD=stack which uses Stack. More documentation on each
@@ -31,38 +35,26 @@ cache:
3135
# addons: {apt: {packages: [libfcgi-dev,libgmp-dev]}}
3236
matrix:
3337
include:
34-
# We grab the appropriate GHC and cabal-install versions from hvr's PPA. See:
35-
# https://github.com/hvr/multi-ghc-travis
36-
#- env: BUILD=cabal GHCVER=7.0.4 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
37-
# compiler: ": #GHC 7.0.4"
38-
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.0.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
39-
#- env: BUILD=cabal GHCVER=7.2.2 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
40-
# compiler: ": #GHC 7.2.2"
41-
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.2.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
42-
#- env: BUILD=cabal GHCVER=7.4.2 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
43-
# compiler: ": #GHC 7.4.2"
44-
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.4.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
45-
#- env: BUILD=cabal GHCVER=7.6.3 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
46-
# compiler: ": #GHC 7.6.3"
47-
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
48-
#- env: BUILD=cabal GHCVER=7.8.4 CABALVER=1.18 HAPPYVER=1.19.5 ALEXVER=3.1.7
49-
# compiler: ": #GHC 7.8.4"
50-
# addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
51-
#- env: BUILD=cabal GHCVER=7.10.3 CABALVER=1.22 HAPPYVER=1.19.5 ALEXVER=3.1.7
52-
# compiler: ": #GHC 7.10.3"
53-
# addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
54-
- env: BUILD=cabal GHCVER=8.0.2 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
55-
compiler: ": #GHC 8.0.2"
56-
addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
38+
- env: BUILD=integration-tests
39+
compiler: ": #integration-tests"
40+
addons: {apt: {packages: [libgmp-dev]}}
41+
42+
- env: BUILD=stack ARGS=""
43+
compiler: ": #stack default"
44+
addons: {apt: {packages: [libgmp-dev]}}
45+
46+
# - env: BUILD=cabal GHCVER=8.0.2 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
47+
# compiler: ": #GHC 8.0.2"
48+
# addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
5749
- env: BUILD=cabal GHCVER=8.2.2 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
5850
compiler: ": #GHC 8.2.2"
5951
addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
6052
- env: BUILD=cabal GHCVER=8.4.4 CABALVER=2.2 HAPPYVER=1.19.5 ALEXVER=3.1.7
6153
compiler: ": #GHC 8.4.4"
6254
addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
63-
- env: BUILD=cabal GHCVER=8.6.3 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7
64-
compiler: ": #GHC 8.6.3"
65-
addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
55+
- env: BUILD=cabal GHCVER=8.6.5 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7
56+
compiler: ": #GHC 8.6.5"
57+
addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
6658

6759
# Build with the newest GHC and cabal-install. This is an accepted failure,
6860
# see below.
@@ -72,30 +64,6 @@ matrix:
7264

7365
# The Stack builds. We can pass in arbitrary Stack arguments via the ARGS
7466
# variable, such as using --stack-yaml to point to a different file.
75-
- env: BUILD=stack ARGS=""
76-
compiler: ": #stack default"
77-
addons: {apt: {packages: [libgmp-dev]}}
78-
79-
#- env: BUILD=stack ARGS="--resolver lts-2"
80-
# compiler: ": #stack 7.8.4"
81-
# addons: {apt: {packages: [libgmp-dev]}}
82-
83-
#- env: BUILD=stack ARGS="--resolver lts-3"
84-
# compiler: ": #stack 7.10.2"
85-
# addons: {apt: {packages: [libgmp-dev]}}
86-
87-
#- env: BUILD=stack ARGS="--resolver lts-6"
88-
# compiler: ": #stack 7.10.3"
89-
# addons: {apt: {packages: [libgmp-dev]}}
90-
91-
#- env: BUILD=stack ARGS="--resolver lts-7"
92-
# compiler: ": #stack 8.0.1"
93-
# addons: {apt: {packages: [libgmp-dev]}}
94-
95-
#- env: BUILD=stack ARGS="--resolver lts-9"
96-
# compiler: ": #stack 8.0.2"
97-
# addons: {apt: {packages: [libgmp-dev]}}
98-
9967
- env: BUILD=stack ARGS="--resolver lts-11 --stack-yaml stack-8.2.2.yaml"
10068
compiler: ": #stack 8.2.2"
10169
addons: {apt: {packages: [libgmp-dev]}}
@@ -104,8 +72,8 @@ matrix:
10472
compiler: ": #stack 8.4.4"
10573
addons: {apt: {packages: [libgmp-dev]}}
10674

107-
- env: BUILD=stack ARGS="--resolver lts-13"
108-
compiler: ": #stack 8.6.3"
75+
- env: BUILD=stack ARGS="--resolver lts-14"
76+
compiler: ": #stack 8.6.5"
10977
addons: {apt: {packages: [libgmp-dev]}}
11078

11179
# Nightly builds are allowed to fail
@@ -118,27 +86,6 @@ matrix:
11886
compiler: ": #stack default osx"
11987
os: osx
12088

121-
# Travis includes an macOS which is incompatible with GHC 7.8.4
122-
#- env: BUILD=stack ARGS="--resolver lts-2"
123-
# compiler: ": #stack 7.8.4 osx"
124-
# os: osx
125-
126-
#- env: BUILD=stack ARGS="--resolver lts-3"
127-
# compiler: ": #stack 7.10.2 osx"
128-
# os: osx
129-
130-
#- env: BUILD=stack ARGS="--resolver lts-6"
131-
# compiler: ": #stack 7.10.3 osx"
132-
# os: osx
133-
134-
#- env: BUILD=stack ARGS="--resolver lts-7"
135-
# compiler: ": #stack 8.0.1 osx"
136-
# os: osx
137-
138-
#- env: BUILD=stack ARGS="--resolver lts-9"
139-
# compiler: ": #stack 8.0.2 osx"
140-
# os: osx
141-
14289
- env: BUILD=stack ARGS="--resolver lts-11 --stack-yaml stack-8.2.2.yaml"
14390
compiler: ": #stack 8.2.2 osx"
14491
os: osx
@@ -147,8 +94,8 @@ matrix:
14794
compiler: ": #stack 8.4.4 osx"
14895
os: osx
14996

150-
- env: BUILD=stack ARGS="--resolver lts-13"
151-
compiler: ": #stack 8.6.3 osx"
97+
- env: BUILD=stack ARGS="--resolver lts-14"
98+
compiler: ": #stack 8.6.5 osx"
15299
os: osx
153100

154101
- env: BUILD=stack ARGS="--resolver nightly"
@@ -202,7 +149,7 @@ install:
202149
# stack --no-terminal $ARGS solver --update-config)
203150
204151
# Build the dependencies
205-
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies
152+
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies --fast
206153
;;
207154
cabal)
208155
cabal --version
@@ -211,10 +158,22 @@ install:
211158
# Get the list of packages from the stack.yaml file. Note that
212159
# this will also implicitly run hpack as necessary to generate
213160
# the .cabal files needed by cabal-install.
214-
PACKAGES=$(stack --install-ghc query locals | grep '^ *path' | sed 's@^ *path:@@')
161+
PACKAGES=$(stack --system-ghc query locals | grep '^ *path' | sed 's@^ *path:@@')
215162
216163
cabal install --only-dependencies --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES
217164
;;
165+
integration-tests)
166+
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
167+
# Download and install kind
168+
curl -LO https://github.com/kubernetes-sigs/kind/releases/download/v0.5.1/kind-linux-amd64 && chmod +x kind-linux-amd64 && sudo mv kind-linux-amd64 /usr/local/bin/kind
169+
# Create a new Kubernetes cluster using KinD
170+
kind create cluster
171+
172+
# Set KUBECONFIG environment variable
173+
export KUBECONFIG="$(kind get kubeconfig-path)"
174+
175+
stack --no-terminal --install-ghc --stack-yaml ./examples/stack.yaml --work-dir .stack-work build --only-dependencies --fast
176+
;;
218177
esac
219178
set +ex
220179
@@ -223,7 +182,7 @@ script:
223182
set -ex
224183
case "$BUILD" in
225184
stack)
226-
stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps
185+
stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps --fast
227186
;;
228187
cabal)
229188
cabal install --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES
@@ -249,5 +208,23 @@ script:
249208
cd $ORIGDIR
250209
done
251210
;;
211+
integration-tests)
212+
EXAMPLE_ARGS="--no-terminal --install-ghc --stack-yaml ./examples/stack.yaml --work-dir .stack-work"
213+
stack $EXAMPLE_ARGS build --fast
214+
215+
# Run simple test
216+
stack $EXAMPLE_ARGS exec simple
217+
218+
# Build and load the in-cluster-example image
219+
cp "$(stack $EXAMPLE_ARGS exec which in-cluster)" in-cluster-example
220+
docker build . -f ./examples/in-cluster/Dockerfile -t in-cluster-example:latest
221+
kind load docker-image in-cluster-example:latest
222+
223+
# Wait for kind node to be ready
224+
kubectl wait --for=condition=Ready node --all
225+
226+
# Run the test pod
227+
./examples/in-cluster/run-test.sh
228+
;;
252229
esac
253230
set +ex

examples/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist
2+
dist-newstyle
3+
*.cabal
4+
.stack-work

examples/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE

examples/in-cluster/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM ubuntu:xenial
2+
3+
RUN apt-get update && apt-get install -y libgmp3-dev
4+
5+
COPY in-cluster-example /usr/local/bin

examples/in-cluster/Main.hs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Main where
3+
4+
import Control.Concurrent.STM
5+
import Control.Exception.Safe
6+
import Kubernetes.Client
7+
import Kubernetes.OpenAPI
8+
import Kubernetes.OpenAPI.API.CoreV1
9+
import Network.HTTP.Client
10+
import Network.HTTP.Types.Status
11+
12+
import qualified Data.Map as Map
13+
import qualified Data.Text as T
14+
import qualified Data.Text.IO as T
15+
16+
main :: IO ()
17+
main = do
18+
oidcCache <- newTVarIO $ Map.fromList []
19+
(manager, cfg) <- mkKubeClientConfig oidcCache KubeConfigCluster
20+
let createNamespaceRequest =
21+
createNamespace (ContentType MimeJSON) (Accept MimeJSON) testNamespace
22+
createdNS <- assertMimeSuccess =<< dispatchMime manager cfg createNamespaceRequest
23+
nsName <- assertJust "Expected K8s to generate name for namespace, but it didn't"
24+
$ (v1ObjectMetaName =<< v1NamespaceMetadata createdNS)
25+
T.putStrLn $ "Created Namespace: " <> nsName
26+
27+
-- NOTE: We cannot use dispatchMime due to this issue: https://github.com/kubernetes/kubernetes/issues/59501
28+
let deleteNamespaceRequest =
29+
deleteNamespace (ContentType MimeJSON) (Accept MimeJSON) (Name nsName)
30+
deleteNamespaceResponse <- dispatchLbs manager cfg deleteNamespaceRequest
31+
if responseStatus deleteNamespaceResponse /= status200
32+
then throwM $ AssertionFailure
33+
$ "Failed to cleanup namespace: " <> T.unpack nsName
34+
<> "\nStatus Code: " <> show (responseStatus deleteNamespaceResponse)
35+
<> "\nBody: " <> show (responseBody deleteNamespaceResponse)
36+
else return ()
37+
putStrLn "Clenaup complete!"
38+
39+
testDeployment :: V1Deployment
40+
testDeployment =
41+
let labelSelector =
42+
mkV1LabelSelector
43+
{ v1LabelSelectorMatchLabels =
44+
Just $ Map.fromList [("app", "test")] }
45+
container =
46+
(mkV1Container "container-name")
47+
{ v1ContainerImage = Just $ "nginx" }
48+
podTemplate =
49+
mkV1PodTemplateSpec
50+
{ v1PodTemplateSpecMetadata =
51+
Just $ mkV1ObjectMeta
52+
{ v1ObjectMetaLabels = Just $ Map.fromList [("app", "test")] }
53+
, v1PodTemplateSpecSpec =
54+
Just $
55+
mkV1PodSpec [container]
56+
}
57+
in mkV1Deployment
58+
{ v1DeploymentMetadata =
59+
Just $ mkV1ObjectMeta { v1ObjectMetaName = Just "test-deployment" }
60+
, v1DeploymentSpec =
61+
Just
62+
$ (mkV1DeploymentSpec labelSelector podTemplate)
63+
}
64+
65+
testNamespace :: V1Namespace
66+
testNamespace =
67+
let nsMetadata =
68+
mkV1ObjectMeta
69+
{ v1ObjectMetaGenerateName = Just "haskell-client-test-" }
70+
in mkV1Namespace
71+
{ v1NamespaceMetadata = Just nsMetadata }
72+
73+
assertMimeSuccess :: MonadThrow m => MimeResult a -> m a
74+
assertMimeSuccess (MimeResult (Right res) _) = pure res
75+
assertMimeSuccess (MimeResult (Left err) _) =
76+
throwM $ AssertionFailure $ "Unexpected MimeError: " ++ show err
77+
78+
assertJust :: MonadThrow m => String -> Maybe a -> m a
79+
assertJust err Nothing = throwM $ AssertionFailure err
80+
assertJust _ (Just x) = return x
81+
82+
data AssertionFailure = AssertionFailure String
83+
deriving Show
84+
85+
instance Exception AssertionFailure

examples/in-cluster/run-test.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
6+
MAX_SECONDS=20
7+
8+
main(){
9+
kubectl apply -f "$SCRIPT_DIR/test-pod.yaml"
10+
start_time="$(date +%s)"
11+
while true; do
12+
phase="$(get-pod-phase in-cluster-example)"
13+
consumed_seconds="$(seconds-since $start_time)"
14+
15+
if [[ "$phase" == "Succeeded" ]]; then
16+
echo "------------------------------"
17+
echo "Test passed!"
18+
echo "------------------------------"
19+
echo
20+
echo "------------------------------"
21+
echo "Logs from test:"
22+
echo "------------------------------"
23+
kubectl logs in-cluster-example
24+
exit 0
25+
elif [[ "$phase" == "Failed" ]]; then
26+
echo "------------------------------"
27+
echo "Test failed!"
28+
echo "------------------------------"
29+
print-failure in-cluster-example
30+
exit 1
31+
elif (( consumed_seconds > MAX_SECONDS )); then
32+
echo "------------------------------"
33+
echo "Test timed out after $MAX_SECONDS seconds!"
34+
echo "------------------------------"
35+
print-failure in-cluster-example
36+
exit 2
37+
else
38+
echo "Test still running, pod phase = $phase"
39+
sleep 0.5
40+
fi
41+
done
42+
}
43+
44+
get-pod-phase() {
45+
kubectl get pod $1 -o 'jsonpath={.status.phase}'
46+
}
47+
48+
print-failure() {
49+
echo
50+
echo "------------------------------"
51+
echo "Pod Description:"
52+
echo "------------------------------"
53+
kubectl describe pod $1
54+
echo
55+
echo "------------------------------"
56+
echo "Logs from test:"
57+
echo "------------------------------"
58+
kubectl logs $1
59+
}
60+
61+
# Takes epoch time
62+
seconds-since() {
63+
local start=$1
64+
local end=$(date +%s)
65+
echo $(( end - start))
66+
}
67+
68+
main

0 commit comments

Comments
 (0)