Merge pull request #61 from akshaymankar/integration-tests
Add Integration tests
This commit is contained in:
133
.travis.yml
133
.travis.yml
@@ -11,6 +11,9 @@
|
||||
# Do not choose a language; we provide our own build tools.
|
||||
language: generic
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
# Caching so the next build will be fast too.
|
||||
cache:
|
||||
directories:
|
||||
@@ -18,6 +21,7 @@ cache:
|
||||
- $HOME/.cabal
|
||||
- $HOME/.stack
|
||||
- $TRAVIS_BUILD_DIR/.stack-work
|
||||
- $TRAVIS_BUILD_DIR/examples/.stack-work
|
||||
|
||||
# The different configurations we want to test. We have BUILD=cabal which uses
|
||||
# cabal-install, and BUILD=stack which uses Stack. More documentation on each
|
||||
@@ -31,38 +35,26 @@ cache:
|
||||
# addons: {apt: {packages: [libfcgi-dev,libgmp-dev]}}
|
||||
matrix:
|
||||
include:
|
||||
# We grab the appropriate GHC and cabal-install versions from hvr's PPA. See:
|
||||
# https://github.com/hvr/multi-ghc-travis
|
||||
#- env: BUILD=cabal GHCVER=7.0.4 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.0.4"
|
||||
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.0.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
#- env: BUILD=cabal GHCVER=7.2.2 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.2.2"
|
||||
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.2.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
#- env: BUILD=cabal GHCVER=7.4.2 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.4.2"
|
||||
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.4.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
#- env: BUILD=cabal GHCVER=7.6.3 CABALVER=1.16 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.6.3"
|
||||
# addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
#- env: BUILD=cabal GHCVER=7.8.4 CABALVER=1.18 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.8.4"
|
||||
# addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
#- env: BUILD=cabal GHCVER=7.10.3 CABALVER=1.22 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 7.10.3"
|
||||
# addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=cabal GHCVER=8.0.2 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
compiler: ": #GHC 8.0.2"
|
||||
addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=integration-tests
|
||||
compiler: ": #integration-tests"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
- env: BUILD=stack ARGS=""
|
||||
compiler: ": #stack default"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
# - env: BUILD=cabal GHCVER=8.0.2 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
# compiler: ": #GHC 8.0.2"
|
||||
# addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=cabal GHCVER=8.2.2 CABALVER=2.0 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
compiler: ": #GHC 8.2.2"
|
||||
addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=cabal GHCVER=8.4.4 CABALVER=2.2 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
compiler: ": #GHC 8.4.4"
|
||||
addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=cabal GHCVER=8.6.3 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
compiler: ": #GHC 8.6.3"
|
||||
addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.3,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
- env: BUILD=cabal GHCVER=8.6.5 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7
|
||||
compiler: ": #GHC 8.6.5"
|
||||
addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5,happy-1.19.5,alex-3.1.7], sources: [hvr-ghc]}}
|
||||
|
||||
# Build with the newest GHC and cabal-install. This is an accepted failure,
|
||||
# see below.
|
||||
@@ -72,30 +64,6 @@ matrix:
|
||||
|
||||
# The Stack builds. We can pass in arbitrary Stack arguments via the ARGS
|
||||
# variable, such as using --stack-yaml to point to a different file.
|
||||
- env: BUILD=stack ARGS=""
|
||||
compiler: ": #stack default"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-2"
|
||||
# compiler: ": #stack 7.8.4"
|
||||
# addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-3"
|
||||
# compiler: ": #stack 7.10.2"
|
||||
# addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-6"
|
||||
# compiler: ": #stack 7.10.3"
|
||||
# addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-7"
|
||||
# compiler: ": #stack 8.0.1"
|
||||
# addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-9"
|
||||
# compiler: ": #stack 8.0.2"
|
||||
# addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
- env: BUILD=stack ARGS="--resolver lts-11 --stack-yaml stack-8.2.2.yaml"
|
||||
compiler: ": #stack 8.2.2"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
@@ -104,8 +72,8 @@ matrix:
|
||||
compiler: ": #stack 8.4.4"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
- env: BUILD=stack ARGS="--resolver lts-13"
|
||||
compiler: ": #stack 8.6.3"
|
||||
- env: BUILD=stack ARGS="--resolver lts-14"
|
||||
compiler: ": #stack 8.6.5"
|
||||
addons: {apt: {packages: [libgmp-dev]}}
|
||||
|
||||
# Nightly builds are allowed to fail
|
||||
@@ -118,27 +86,6 @@ matrix:
|
||||
compiler: ": #stack default osx"
|
||||
os: osx
|
||||
|
||||
# Travis includes an macOS which is incompatible with GHC 7.8.4
|
||||
#- env: BUILD=stack ARGS="--resolver lts-2"
|
||||
# compiler: ": #stack 7.8.4 osx"
|
||||
# os: osx
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-3"
|
||||
# compiler: ": #stack 7.10.2 osx"
|
||||
# os: osx
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-6"
|
||||
# compiler: ": #stack 7.10.3 osx"
|
||||
# os: osx
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-7"
|
||||
# compiler: ": #stack 8.0.1 osx"
|
||||
# os: osx
|
||||
|
||||
#- env: BUILD=stack ARGS="--resolver lts-9"
|
||||
# compiler: ": #stack 8.0.2 osx"
|
||||
# os: osx
|
||||
|
||||
- env: BUILD=stack ARGS="--resolver lts-11 --stack-yaml stack-8.2.2.yaml"
|
||||
compiler: ": #stack 8.2.2 osx"
|
||||
os: osx
|
||||
@@ -147,8 +94,8 @@ matrix:
|
||||
compiler: ": #stack 8.4.4 osx"
|
||||
os: osx
|
||||
|
||||
- env: BUILD=stack ARGS="--resolver lts-13"
|
||||
compiler: ": #stack 8.6.3 osx"
|
||||
- env: BUILD=stack ARGS="--resolver lts-14"
|
||||
compiler: ": #stack 8.6.5 osx"
|
||||
os: osx
|
||||
|
||||
- env: BUILD=stack ARGS="--resolver nightly"
|
||||
@@ -202,7 +149,7 @@ install:
|
||||
# stack --no-terminal $ARGS solver --update-config)
|
||||
|
||||
# Build the dependencies
|
||||
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies
|
||||
stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies --fast
|
||||
;;
|
||||
cabal)
|
||||
cabal --version
|
||||
@@ -211,10 +158,22 @@ install:
|
||||
# Get the list of packages from the stack.yaml file. Note that
|
||||
# this will also implicitly run hpack as necessary to generate
|
||||
# the .cabal files needed by cabal-install.
|
||||
PACKAGES=$(stack --install-ghc query locals | grep '^ *path' | sed 's@^ *path:@@')
|
||||
PACKAGES=$(stack --system-ghc query locals | grep '^ *path' | sed 's@^ *path:@@')
|
||||
|
||||
cabal install --only-dependencies --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES
|
||||
;;
|
||||
integration-tests)
|
||||
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/
|
||||
# Download and install kind
|
||||
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
|
||||
# Create a new Kubernetes cluster using KinD
|
||||
kind create cluster
|
||||
|
||||
# Set KUBECONFIG environment variable
|
||||
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||
|
||||
stack --no-terminal --install-ghc --stack-yaml ./examples/stack.yaml --work-dir .stack-work build --only-dependencies --fast
|
||||
;;
|
||||
esac
|
||||
set +ex
|
||||
|
||||
@@ -223,7 +182,7 @@ script:
|
||||
set -ex
|
||||
case "$BUILD" in
|
||||
stack)
|
||||
stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps
|
||||
stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps --fast
|
||||
;;
|
||||
cabal)
|
||||
cabal install --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES
|
||||
@@ -249,5 +208,23 @@ script:
|
||||
cd $ORIGDIR
|
||||
done
|
||||
;;
|
||||
integration-tests)
|
||||
EXAMPLE_ARGS="--no-terminal --install-ghc --stack-yaml ./examples/stack.yaml --work-dir .stack-work"
|
||||
stack $EXAMPLE_ARGS build --fast
|
||||
|
||||
# Run simple test
|
||||
stack $EXAMPLE_ARGS exec simple
|
||||
|
||||
# Build and load the in-cluster-example image
|
||||
cp "$(stack $EXAMPLE_ARGS exec which in-cluster)" in-cluster-example
|
||||
docker build . -f ./examples/in-cluster/Dockerfile -t in-cluster-example:latest
|
||||
kind load docker-image in-cluster-example:latest
|
||||
|
||||
# Wait for kind node to be ready
|
||||
kubectl wait --for=condition=Ready node --all
|
||||
|
||||
# Run the test pod
|
||||
./examples/in-cluster/run-test.sh
|
||||
;;
|
||||
esac
|
||||
set +ex
|
||||
|
||||
4
examples/.gitignore
vendored
Normal file
4
examples/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
dist
|
||||
dist-newstyle
|
||||
*.cabal
|
||||
.stack-work
|
||||
1
examples/LICENSE
Symbolic link
1
examples/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE
|
||||
5
examples/in-cluster/Dockerfile
Normal file
5
examples/in-cluster/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM ubuntu:xenial
|
||||
|
||||
RUN apt-get update && apt-get install -y libgmp3-dev
|
||||
|
||||
COPY in-cluster-example /usr/local/bin
|
||||
85
examples/in-cluster/Main.hs
Normal file
85
examples/in-cluster/Main.hs
Normal file
@@ -0,0 +1,85 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main where
|
||||
|
||||
import Control.Concurrent.STM
|
||||
import Control.Exception.Safe
|
||||
import Kubernetes.Client
|
||||
import Kubernetes.OpenAPI
|
||||
import Kubernetes.OpenAPI.API.CoreV1
|
||||
import Network.HTTP.Client
|
||||
import Network.HTTP.Types.Status
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
oidcCache <- newTVarIO $ Map.fromList []
|
||||
(manager, cfg) <- mkKubeClientConfig oidcCache KubeConfigCluster
|
||||
let createNamespaceRequest =
|
||||
createNamespace (ContentType MimeJSON) (Accept MimeJSON) testNamespace
|
||||
createdNS <- assertMimeSuccess =<< dispatchMime manager cfg createNamespaceRequest
|
||||
nsName <- assertJust "Expected K8s to generate name for namespace, but it didn't"
|
||||
$ (v1ObjectMetaName =<< v1NamespaceMetadata createdNS)
|
||||
T.putStrLn $ "Created Namespace: " <> nsName
|
||||
|
||||
-- NOTE: We cannot use dispatchMime due to this issue: https://github.com/kubernetes/kubernetes/issues/59501
|
||||
let deleteNamespaceRequest =
|
||||
deleteNamespace (ContentType MimeJSON) (Accept MimeJSON) (Name nsName)
|
||||
deleteNamespaceResponse <- dispatchLbs manager cfg deleteNamespaceRequest
|
||||
if responseStatus deleteNamespaceResponse /= status200
|
||||
then throwM $ AssertionFailure
|
||||
$ "Failed to cleanup namespace: " <> T.unpack nsName
|
||||
<> "\nStatus Code: " <> show (responseStatus deleteNamespaceResponse)
|
||||
<> "\nBody: " <> show (responseBody deleteNamespaceResponse)
|
||||
else return ()
|
||||
putStrLn "Clenaup complete!"
|
||||
|
||||
testDeployment :: V1Deployment
|
||||
testDeployment =
|
||||
let labelSelector =
|
||||
mkV1LabelSelector
|
||||
{ v1LabelSelectorMatchLabels =
|
||||
Just $ Map.fromList [("app", "test")] }
|
||||
container =
|
||||
(mkV1Container "container-name")
|
||||
{ v1ContainerImage = Just $ "nginx" }
|
||||
podTemplate =
|
||||
mkV1PodTemplateSpec
|
||||
{ v1PodTemplateSpecMetadata =
|
||||
Just $ mkV1ObjectMeta
|
||||
{ v1ObjectMetaLabels = Just $ Map.fromList [("app", "test")] }
|
||||
, v1PodTemplateSpecSpec =
|
||||
Just $
|
||||
mkV1PodSpec [container]
|
||||
}
|
||||
in mkV1Deployment
|
||||
{ v1DeploymentMetadata =
|
||||
Just $ mkV1ObjectMeta { v1ObjectMetaName = Just "test-deployment" }
|
||||
, v1DeploymentSpec =
|
||||
Just
|
||||
$ (mkV1DeploymentSpec labelSelector podTemplate)
|
||||
}
|
||||
|
||||
testNamespace :: V1Namespace
|
||||
testNamespace =
|
||||
let nsMetadata =
|
||||
mkV1ObjectMeta
|
||||
{ v1ObjectMetaGenerateName = Just "haskell-client-test-" }
|
||||
in mkV1Namespace
|
||||
{ v1NamespaceMetadata = Just nsMetadata }
|
||||
|
||||
assertMimeSuccess :: MonadThrow m => MimeResult a -> m a
|
||||
assertMimeSuccess (MimeResult (Right res) _) = pure res
|
||||
assertMimeSuccess (MimeResult (Left err) _) =
|
||||
throwM $ AssertionFailure $ "Unexpected MimeError: " ++ show err
|
||||
|
||||
assertJust :: MonadThrow m => String -> Maybe a -> m a
|
||||
assertJust err Nothing = throwM $ AssertionFailure err
|
||||
assertJust _ (Just x) = return x
|
||||
|
||||
data AssertionFailure = AssertionFailure String
|
||||
deriving Show
|
||||
|
||||
instance Exception AssertionFailure
|
||||
68
examples/in-cluster/run-test.sh
Executable file
68
examples/in-cluster/run-test.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
MAX_SECONDS=20
|
||||
|
||||
main(){
|
||||
kubectl apply -f "$SCRIPT_DIR/test-pod.yaml"
|
||||
start_time="$(date +%s)"
|
||||
while true; do
|
||||
phase="$(get-pod-phase in-cluster-example)"
|
||||
consumed_seconds="$(seconds-since $start_time)"
|
||||
|
||||
if [[ "$phase" == "Succeeded" ]]; then
|
||||
echo "------------------------------"
|
||||
echo "Test passed!"
|
||||
echo "------------------------------"
|
||||
echo
|
||||
echo "------------------------------"
|
||||
echo "Logs from test:"
|
||||
echo "------------------------------"
|
||||
kubectl logs in-cluster-example
|
||||
exit 0
|
||||
elif [[ "$phase" == "Failed" ]]; then
|
||||
echo "------------------------------"
|
||||
echo "Test failed!"
|
||||
echo "------------------------------"
|
||||
print-failure in-cluster-example
|
||||
exit 1
|
||||
elif (( consumed_seconds > MAX_SECONDS )); then
|
||||
echo "------------------------------"
|
||||
echo "Test timed out after $MAX_SECONDS seconds!"
|
||||
echo "------------------------------"
|
||||
print-failure in-cluster-example
|
||||
exit 2
|
||||
else
|
||||
echo "Test still running, pod phase = $phase"
|
||||
sleep 0.5
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
get-pod-phase() {
|
||||
kubectl get pod $1 -o 'jsonpath={.status.phase}'
|
||||
}
|
||||
|
||||
print-failure() {
|
||||
echo
|
||||
echo "------------------------------"
|
||||
echo "Pod Description:"
|
||||
echo "------------------------------"
|
||||
kubectl describe pod $1
|
||||
echo
|
||||
echo "------------------------------"
|
||||
echo "Logs from test:"
|
||||
echo "------------------------------"
|
||||
kubectl logs $1
|
||||
}
|
||||
|
||||
# Takes epoch time
|
||||
seconds-since() {
|
||||
local start=$1
|
||||
local end=$(date +%s)
|
||||
echo $(( end - start))
|
||||
}
|
||||
|
||||
main
|
||||
34
examples/in-cluster/test-pod.yaml
Normal file
34
examples/in-cluster/test-pod.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: in-cluster-example
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: in-cluster-example
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: in-cluster-example
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: in-cluster-example
|
||||
namespace: default
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
serviceAccountName: in-cluster-example
|
||||
containers:
|
||||
- name: in-cluster-example
|
||||
image: in-cluster-example:latest
|
||||
imagePullPolicy: Never
|
||||
command:
|
||||
- in-cluster-example
|
||||
33
examples/package.yaml
Normal file
33
examples/package.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
name: kubernetes-examples
|
||||
version: 0.1.0.1
|
||||
description: |
|
||||
Examples to interact with Kubernetes using kubernetes-client and kubernetes-client-core
|
||||
synopsis: Kubernetes examples with Haskell
|
||||
maintainer:
|
||||
- Shimin Guo <smguo2001@gmail.com>
|
||||
- Akshay Mankar <itsakshaymankar@gmail.com>
|
||||
category: Examples, Kubernetes
|
||||
license: Apache-2.0
|
||||
license-file: LICENSE
|
||||
|
||||
executables:
|
||||
simple:
|
||||
main: Main.hs
|
||||
source-dirs: simple
|
||||
ghc-options:
|
||||
- -Wall
|
||||
in-cluster:
|
||||
main: Main.hs
|
||||
source-dirs: in-cluster
|
||||
ghc-options:
|
||||
- -Wall
|
||||
dependencies:
|
||||
- base
|
||||
- containers
|
||||
- http-client
|
||||
- http-types
|
||||
- kubernetes-client
|
||||
- kubernetes-client-core
|
||||
- safe-exceptions
|
||||
- stm
|
||||
- text
|
||||
102
examples/simple/Main.hs
Normal file
102
examples/simple/Main.hs
Normal file
@@ -0,0 +1,102 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module Main where
|
||||
|
||||
import Control.Concurrent.STM
|
||||
import Control.Exception.Safe
|
||||
import Kubernetes.Client
|
||||
import Kubernetes.OpenAPI
|
||||
import Kubernetes.OpenAPI.API.AppsV1
|
||||
import Kubernetes.OpenAPI.API.CoreV1
|
||||
import Network.HTTP.Client
|
||||
import Network.HTTP.Types.Status
|
||||
import System.Environment
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
kubeConfigFile <- getEnv "KUBECONFIG"
|
||||
oidcCache <- newTVarIO $ Map.fromList []
|
||||
(manager, cfg) <- mkKubeClientConfig oidcCache
|
||||
$ KubeConfigFile kubeConfigFile
|
||||
let createNamespaceRequest =
|
||||
createNamespace (ContentType MimeJSON) (Accept MimeJSON) testNamespace
|
||||
createdNS <- assertMimeSuccess =<< dispatchMime manager cfg createNamespaceRequest
|
||||
nsName <- assertJust "Expected K8s to generate name for namespace, but it didn't"
|
||||
$ (v1ObjectMetaName =<< v1NamespaceMetadata createdNS)
|
||||
T.putStrLn $ "Created Namespace: " <> nsName
|
||||
|
||||
let createDeploymentRequest =
|
||||
createNamespacedDeployment (ContentType MimeJSON) (Accept MimeJSON) testDeployment (Namespace nsName)
|
||||
deployment <- assertMimeSuccess =<< dispatchMime manager cfg createDeploymentRequest
|
||||
T.putStrLn $ "Created Deployment: " <> maybe "No name!?" id (v1DeploymentMetadata deployment >>= v1ObjectMetaName)
|
||||
|
||||
let listDeploymentsRequest =
|
||||
listNamespacedDeployment (Accept MimeJSON) (Namespace nsName)
|
||||
listedDeployments <- assertMimeSuccess =<< dispatchMime manager cfg listDeploymentsRequest
|
||||
let numberOfDeployments = length $ v1DeploymentListItems listedDeployments
|
||||
if numberOfDeployments /= 1
|
||||
then throwM $ AssertionFailure $ "Expected 1 deployment, found: " <> show numberOfDeployments
|
||||
else putStrLn "Test successful!"
|
||||
|
||||
-- NOTE: We cannot use dispatchMime due to this issue: https://github.com/kubernetes/kubernetes/issues/59501
|
||||
let deleteNamespaceRequest =
|
||||
deleteNamespace (ContentType MimeJSON) (Accept MimeJSON) (Name nsName)
|
||||
deleteNamespaceResponse <- dispatchLbs manager cfg deleteNamespaceRequest
|
||||
if responseStatus deleteNamespaceResponse /= status200
|
||||
then throwM $ AssertionFailure
|
||||
$ "Failed to cleanup namespace: " <> T.unpack nsName
|
||||
<> "\nStatus Code: " <> show (responseStatus deleteNamespaceResponse)
|
||||
<> "\nBody: " <> show (responseBody deleteNamespaceResponse)
|
||||
else return ()
|
||||
putStrLn "Clenaup complete!"
|
||||
|
||||
testDeployment :: V1Deployment
|
||||
testDeployment =
|
||||
let labelSelector =
|
||||
mkV1LabelSelector
|
||||
{ v1LabelSelectorMatchLabels =
|
||||
Just $ Map.fromList [("app", "test")] }
|
||||
container =
|
||||
(mkV1Container "container-name")
|
||||
{ v1ContainerImage = Just $ "nginx" }
|
||||
podTemplate =
|
||||
mkV1PodTemplateSpec
|
||||
{ v1PodTemplateSpecMetadata =
|
||||
Just $ mkV1ObjectMeta
|
||||
{ v1ObjectMetaLabels = Just $ Map.fromList [("app", "test")] }
|
||||
, v1PodTemplateSpecSpec =
|
||||
Just $
|
||||
mkV1PodSpec [container]
|
||||
}
|
||||
in mkV1Deployment
|
||||
{ v1DeploymentMetadata =
|
||||
Just $ mkV1ObjectMeta { v1ObjectMetaName = Just "test-deployment" }
|
||||
, v1DeploymentSpec =
|
||||
Just
|
||||
$ (mkV1DeploymentSpec labelSelector podTemplate)
|
||||
}
|
||||
|
||||
testNamespace :: V1Namespace
|
||||
testNamespace =
|
||||
let nsMetadata =
|
||||
mkV1ObjectMeta
|
||||
{ v1ObjectMetaGenerateName = Just "haskell-client-test-" }
|
||||
in mkV1Namespace
|
||||
{ v1NamespaceMetadata = Just nsMetadata }
|
||||
|
||||
assertMimeSuccess :: MonadThrow m => MimeResult a -> m a
|
||||
assertMimeSuccess (MimeResult (Right res) _) = pure res
|
||||
assertMimeSuccess (MimeResult (Left err) _) =
|
||||
throwM $ AssertionFailure $ "Unexpected MimeError: " ++ show err
|
||||
|
||||
assertJust :: MonadThrow m => String -> Maybe a -> m a
|
||||
assertJust err Nothing = throwM $ AssertionFailure err
|
||||
assertJust _ (Just x) = return x
|
||||
|
||||
data AssertionFailure = AssertionFailure String
|
||||
deriving Show
|
||||
|
||||
instance Exception AssertionFailure
|
||||
13
examples/stack.yaml
Normal file
13
examples/stack.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
resolver: lts-14.7
|
||||
packages:
|
||||
- .
|
||||
- ../kubernetes-client
|
||||
- ../kubernetes
|
||||
extra-deps:
|
||||
- jsonpath-0.1.0.1
|
||||
- jwt-0.10.0
|
||||
- oidc-client-0.4.0.0
|
||||
- git: https://github.com/akshaymankar/hs-certificate.git
|
||||
commit: 2a71b5271b83e87a1fd14c53cc5987bef6e07a06
|
||||
subdirs:
|
||||
- x509-validation
|
||||
49
examples/stack.yaml.lock
Normal file
49
examples/stack.yaml.lock
Normal file
@@ -0,0 +1,49 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: jsonpath-0.1.0.1@sha256:55718ac52b25cd8ce80fbdc9079a112344f032b056cfaf30737a29b6bd1a5c12,2377
|
||||
pantry-tree:
|
||||
size: 1097
|
||||
sha256: 027749c943abaa6a78adc58e5abd4b3f42644c17c5fb7edf816557424a79448b
|
||||
original:
|
||||
hackage: jsonpath-0.1.0.1
|
||||
- completed:
|
||||
hackage: jwt-0.10.0@sha256:d14551b0c357424fb9441ec9a7a9d5b90b13f805fcc9327ba49db548cd64fc29,4180
|
||||
pantry-tree:
|
||||
size: 1027
|
||||
sha256: e0cf95e834d99768ad8a3f7e99246948f0cdd2cfa18813517f540144aea6c3e5
|
||||
original:
|
||||
hackage: jwt-0.10.0
|
||||
- completed:
|
||||
hackage: oidc-client-0.4.0.0@sha256:f72a496ab27d9a5071be44e750718c539118ac52c2f1535a5fb3dde7f9874a55,3306
|
||||
pantry-tree:
|
||||
size: 1153
|
||||
sha256: 68c285c6365360975d50bbb18cb07755d5ef19af8bf0e998d3ea46d35ef4a4e1
|
||||
original:
|
||||
hackage: oidc-client-0.4.0.0
|
||||
- completed:
|
||||
subdir: x509-validation
|
||||
cabal-file:
|
||||
size: 2211
|
||||
sha256: 141d15544dacf1aa3278ba40d622ce78b7caaf523c3ff8c537a70ef902ae6596
|
||||
name: x509-validation
|
||||
version: 1.6.11
|
||||
git: https://github.com/akshaymankar/hs-certificate.git
|
||||
pantry-tree:
|
||||
size: 631
|
||||
sha256: 19855a414ea47001945b06f6b108a80b8ba3378c3ed585026215acd307d809db
|
||||
commit: 2a71b5271b83e87a1fd14c53cc5987bef6e07a06
|
||||
original:
|
||||
subdir: x509-validation
|
||||
git: https://github.com/akshaymankar/hs-certificate.git
|
||||
commit: 2a71b5271b83e87a1fd14c53cc5987bef6e07a06
|
||||
snapshots:
|
||||
- completed:
|
||||
size: 523700
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/7.yaml
|
||||
sha256: 8e3f3c894be74d71fa4bf085e0a8baae7e4d7622d07ea31a52736b80f8b9bb1a
|
||||
original: lts-14.7
|
||||
12
kubernetes/stack.yaml.lock
Normal file
12
kubernetes/stack.yaml.lock
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages: []
|
||||
snapshots:
|
||||
- completed:
|
||||
size: 523700
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/7.yaml
|
||||
sha256: 8e3f3c894be74d71fa4bf085e0a8baae7e4d7622d07ea31a52736b80f8b9bb1a
|
||||
original: lts-14.7
|
||||
Reference in New Issue
Block a user