diff --git a/kubeconfig/package.yaml b/kubeconfig/package.yaml deleted file mode 100644 index 822ea73e..00000000 --- a/kubeconfig/package.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: kubeconfig -version: 0.1.0.0 -description: | - This package contains functions for working with kubeconfig files. - - Usage of kubeconfig files are described at https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ -library: - source-dirs: src -tests: - spec: - main: Spec.hs - source-dirs: test - dependencies: - - hspec - - yaml - - kubeconfig -extra-source-files: - - test/testdata/* -dependencies: - - base >=4.7 && <5.0 - - aeson - - containers - - text diff --git a/kubernetes-client-helper/.gitignore b/kubernetes-client-helper/.gitignore deleted file mode 100644 index 22442ce4..00000000 --- a/kubernetes-client-helper/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.stack-work -src/highlight.js -src/style.css -dist -dist-newstyle -cabal.project.local -.cabal-sandbox -cabal.sandbox.config -*.cabal \ No newline at end of file diff --git a/kubernetes-client-helper/package.yaml b/kubernetes-client-helper/package.yaml deleted file mode 100644 index a8c09ea5..00000000 --- a/kubernetes-client-helper/package.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: kubernetes-client-helper -version: 0.1.0.0 -library: - source-dirs: src -dependencies: - - base >=4.7 && <5.0 - - kubernetes-openapi-client-gen == 0.1.0.0 - - pem - - x509 - - tls - - x509-system - - x509-store - - data-default-class - - connection - - x509-validation - - http-client >=0.5 && <0.6 - - http-client-tls - - microlens >= 0.4.3 && <0.5 - - bytestring >=0.10.0 && <0.11 - - text >=0.11 && <1.3 - - safe-exceptions <0.2 diff --git a/kubeconfig/.gitignore b/kubernetes-client/.gitignore similarity index 93% rename from kubeconfig/.gitignore rename to kubernetes-client/.gitignore index 22442ce4..ddf79f9f 100644 --- a/kubeconfig/.gitignore +++ b/kubernetes-client/.gitignore @@ -6,4 +6,4 @@ dist-newstyle cabal.project.local .cabal-sandbox cabal.sandbox.config -*.cabal \ No newline at end of file +*.cabal diff --git a/kubernetes-client/LICENSE b/kubernetes-client/LICENSE new file mode 120000 index 00000000..ea5b6064 --- /dev/null +++ b/kubernetes-client/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/kubernetes-client/README.md b/kubernetes-client/README.md new file mode 100644 index 00000000..2181a537 --- /dev/null +++ b/kubernetes-client/README.md @@ -0,0 +1,117 @@ +# kubernetes-client + +## Example + +```haskell +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Data.Function ((&)) +import Kubernetes.Client (defaultTLSClientParams, + disableServerCertValidation, + disableServerNameValidation, + disableValidateAuthMethods, + loadPEMCerts, newManager, + setCAStore, setClientCert, + setMasterURI, setTokenAuth) +import Kubernetes.OpenAPI (Accept (..), MimeJSON (..), + dispatchMime, newConfig) +import qualified Kubernetes.OpenAPI.API.CoreV1 as CoreV1 +import Network.TLS (credentialLoadX509) + +main :: IO () +main = do + -- We need to first create a Kubernetes.Core.KubernetesConfig and a Network.HTTP.Client.Manager. + -- Currently we need to construct these objects manually. Work is underway to construct these + -- objects automatically from a kubeconfig file. See https://github.com/kubernetes-client/haskell/issues/2. + kcfg <- + newConfig + & fmap (setMasterURI "https://mycluster.example.com") -- fill in master URI + & fmap (setTokenAuth "mytoken") -- if using token auth + & fmap disableValidateAuthMethods -- if using client cert auth + myCAStore <- loadPEMCerts "/path/to/ca.crt" -- if using custom CA certs + myCert <- -- if using client cert + credentialLoadX509 "/path/to/client.crt" "/path/to/client.key" + >>= either error return + tlsParams <- + defaultTLSClientParams + & fmap disableServerNameValidation -- if master address is specified as an IP address + & fmap disableServerCertValidation -- if you don't want to validate the server cert at all (insecure) + & fmap (setCAStore myCAStore) -- if using custom CA certs + & fmap (setClientCert myCert) -- if using client cert + manager <- newManager tlsParams + dispatchMime + manager + kcfg + (CoreV1.listPodForAllNamespaces (Accept MimeJSON)) + >>= print +``` + +## Watch Example +Following is a simple example which +just streams to stdout. First some setup - this assumes kubernetes is accessible +at http://localhost:8001, e.g. after running `kubectl proxy`: + +```haskell +> import qualified Data.ByteString.Streaming.Char8 as Q + +> manager <- newManager defaultManagerSettings +> defaultConfig <- newConfig +> config = defaultConfig { configHost = "http://localhost:8001", configValidateAuthMethods = False } +> request = listEndpointsForAllNamespaces (Accept MimeJSON) +``` + +Launching 'dispatchWatch' with the above we get a stream of endpoints data: + +```haskell + > dispatchWatch manager config request Q.stdout + {"type":\"ADDED\","object":{"kind":\"Endpoints\","apiVersion":"v1","metadata":{"name":"heapster" .... +``` + +A more complex example involving some ggprocessing of the stream, the following +prints out the event types of each event. First, define functions to allow us apply +a parser to a stream: + + +```haskell +import Data.Aeson +import qualified Data.ByteString.Streaming.Char8 as Q +import Data.JsonStream.Parser +import qualified Streaming.Prelude as S + +-- | Parse the stream using the given parser. +streamParse :: + FromJSON a => + Parser a + -> Q.ByteString IO r + -> Stream (Of [a]) IO r +streamParse parser byteStream = do + byteStream & Q.lines & parseEvent parser + +-- | Parse a single event from the stream. +parseEvent :: + (FromJSON a, Monad m) => + Parser a + -> Stream (Q.ByteString m) m r + -> Stream (Of [a]) m r +parseEvent parser byteStream = S.map (parseByteString parser) (S.mapped Q.toStrict byteStream) +``` + +Next, define the parser and apply it to the stream: + +```haskell +> eventParser = value :: Parser (WatchEvent V1Endpoints) +> withResponseBody body = streamParse eventParser body & S.map (map eventType) +> dispatchWatch manager config request (S.print . withResponseBody) +[\"ADDED\"] +[\"ADDED\"] +[\"MODIFIED\"] +... +``` + +Packages in this example: + * Data.Aeson -- from [aeson](https://hackage.haskell.org/package/aeson) + * Data.ByteString.Streaming.Char8 from [streaming-bytestring](https://hackage.haskell.org/package/streaming-bytestring-0.1.5/docs/Data-ByteString-Streaming-Char8.html) + * Data.JsonStream.Parser from [json-stream](https://hackage.haskell.org/package/json-stream-0.4.1.5/docs/Data-JsonStream-Parser.html) + * Streaming.Prelude from [streaming](https://hackage.haskell.org/package/streaming-0.2.0.0/docs/Streaming-Prelude.html) diff --git a/kubernetes-client-helper/README.md b/kubernetes-client/example/App.hs similarity index 60% rename from kubernetes-client-helper/README.md rename to kubernetes-client/example/App.hs index 6d292d62..6f10379f 100644 --- a/kubernetes-client-helper/README.md +++ b/kubernetes-client/example/App.hs @@ -1,29 +1,22 @@ -# kubernetes-client-helper - -Library of convenience functions for working with the `kubernetes` package. - -## Example - -Include the following packages as dependencies: -- kubernetes -- kubernetes-client-helper -- tls - -```haskell {-# LANGUAGE OverloadedStrings #-} module Main where -import Data.Function ((&)) -import qualified Kubernetes.API.CoreV1 -import Kubernetes.Client (dispatchMime) -import Kubernetes.ClientHelper -import Kubernetes.Core (newConfig) -import Kubernetes.MimeTypes (Accept (..), MimeJSON (..)) -import Network.TLS (credentialLoadX509) +import Data.Function ((&)) +import Kubernetes.Client (defaultTLSClientParams, + disableServerCertValidation, + disableServerNameValidation, + disableValidateAuthMethods, + loadPEMCerts, newManager, + setCAStore, setClientCert, + setMasterURI, setTokenAuth) +import Kubernetes.OpenAPI (Accept (..), MimeJSON (..), + dispatchMime, newConfig) +import qualified Kubernetes.OpenAPI.API.CoreV1 as CoreV1 +import Network.TLS (credentialLoadX509) -main :: IO () -main = do +example :: IO () +example = do -- We need to first create a Kubernetes.Core.KubernetesConfig and a Network.HTTP.Client.Manager. -- Currently we need to construct these objects manually. Work is underway to construct these -- objects automatically from a kubeconfig file. See https://github.com/kubernetes-client/haskell/issues/2. @@ -46,7 +39,8 @@ main = do dispatchMime manager kcfg - (Kubernetes.API.CoreV1.listPodForAllNamespaces (Accept MimeJSON)) + (CoreV1.listPodForAllNamespaces (Accept MimeJSON)) >>= print -``` -s + +main :: IO () +main = return () diff --git a/kubernetes-client/package.yaml b/kubernetes-client/package.yaml new file mode 100644 index 00000000..44f21ad5 --- /dev/null +++ b/kubernetes-client/package.yaml @@ -0,0 +1,49 @@ +name: kubernetes-client +version: 0.1.0.0 +description: | + Client library for interacting with a Kubernetes cluster. + + This package contains hand-written code while kubernetes-client-core contains code auto-generated from the OpenAPI spec. +synopsis: Client library for Kubernetes +maintainer: Shimin Guo +category: Web +license: Apache-2.0 +license-file: LICENSE +library: + source-dirs: src +tests: + spec: + main: Spec.hs + source-dirs: test + dependencies: + - kubernetes-client + - hspec + - yaml + example: + main: App.hs + source-dirs: example + dependencies: + - kubernetes-client +extra-source-files: + - test/testdata/* +dependencies: + - base >=4.7 && <5.0 + - bytestring >=0.10.0 && <0.11 + - aeson >=1.0 && <2.0 + - connection + - containers + - data-default-class + - http-client >=0.5 && <0.6 + - http-client-tls + - kubernetes-openapi-client-gen == 0.1.0.0 + - microlens >= 0.4.3 && <0.5 + - mtl >=2.2.1 + - pem + - safe-exceptions <0.2 + - streaming-bytestring >= 0.1.5 && < 0.2.0 + - text >=0.11 && <1.3 + - tls + - x509 + - x509-system + - x509-store + - x509-validation diff --git a/kubernetes-client/src/Kubernetes/Client.hs b/kubernetes-client/src/Kubernetes/Client.hs new file mode 100644 index 00000000..f6461145 --- /dev/null +++ b/kubernetes-client/src/Kubernetes/Client.hs @@ -0,0 +1,9 @@ +module Kubernetes.Client + ( module Kubernetes.Client.Config + , module Kubernetes.Client.KubeConfig + , module Kubernetes.Client.Watch + ) where + +import Kubernetes.Client.Config +import Kubernetes.Client.KubeConfig +import Kubernetes.Client.Watch diff --git a/kubernetes-client-helper/src/Kubernetes/ClientHelper.hs b/kubernetes-client/src/Kubernetes/Client/Config.hs similarity index 99% rename from kubernetes-client-helper/src/Kubernetes/ClientHelper.hs rename to kubernetes-client/src/Kubernetes/Client/Config.hs index e503c48f..7dd0de21 100644 --- a/kubernetes-client-helper/src/Kubernetes/ClientHelper.hs +++ b/kubernetes-client/src/Kubernetes/Client/Config.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module Kubernetes.ClientHelper where +module Kubernetes.Client.Config where import qualified Kubernetes.OpenAPI.Core as K import qualified Kubernetes.OpenAPI.Model as K diff --git a/kubeconfig/src/Kubernetes/KubeConfig.hs b/kubernetes-client/src/Kubernetes/Client/KubeConfig.hs similarity index 99% rename from kubeconfig/src/Kubernetes/KubeConfig.hs rename to kubernetes-client/src/Kubernetes/Client/KubeConfig.hs index 6bef2e90..d9e71f15 100644 --- a/kubeconfig/src/Kubernetes/KubeConfig.hs +++ b/kubernetes-client/src/Kubernetes/Client/KubeConfig.hs @@ -16,7 +16,7 @@ The official definition of the kubeconfig is defined in https://github.com/kuber This is a mostly straightforward translation into Haskell, with 'FromJSON' and 'ToJSON' instances defined. -} -module Kubernetes.KubeConfig where +module Kubernetes.Client.KubeConfig where import Data.Aeson (FromJSON (..), Options, ToJSON (..), Value (..), camelTo2, defaultOptions, diff --git a/kubernetes-watch/src/Kubernetes/Watch/Client.hs b/kubernetes-client/src/Kubernetes/Client/Watch.hs similarity index 98% rename from kubernetes-watch/src/Kubernetes/Watch/Client.hs rename to kubernetes-client/src/Kubernetes/Client/Watch.hs index 9ae430ca..eede5798 100644 --- a/kubernetes-watch/src/Kubernetes/Watch/Client.hs +++ b/kubernetes-client/src/Kubernetes/Client/Watch.hs @@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -module Kubernetes.Watch.Client +module Kubernetes.Client.Watch ( WatchEvent , eventType , eventObject diff --git a/kubeconfig/test/Spec.hs b/kubernetes-client/test/Spec.hs similarity index 61% rename from kubeconfig/test/Spec.hs rename to kubernetes-client/test/Spec.hs index ceacce13..378cc2e9 100644 --- a/kubeconfig/test/Spec.hs +++ b/kubernetes-client/test/Spec.hs @@ -1,12 +1,14 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -import Data.Aeson (decode, encode, parseJSON, toJSON) -import Data.Maybe (fromJust) -import Data.Yaml (decodeFile) -import Kubernetes.KubeConfig (AuthInfo (..), Cluster (..), Config, - Context (..), getAuthInfo, getCluster, - getContext) +import Data.Aeson (decode, encode, parseJSON, + toJSON) +import Data.Maybe (fromJust) +import Data.Yaml (decodeFile) +import Kubernetes.Client.KubeConfig (AuthInfo (..), Cluster (..), + Config, Context (..), + getAuthInfo, getCluster, + getContext) import Test.Hspec main :: IO () diff --git a/kubeconfig/test/testdata/kubeconfig.yaml b/kubernetes-client/test/testdata/kubeconfig.yaml similarity index 100% rename from kubeconfig/test/testdata/kubeconfig.yaml rename to kubernetes-client/test/testdata/kubeconfig.yaml diff --git a/kubernetes-watch/README.md b/kubernetes-watch/README.md deleted file mode 100644 index 17835bdb..00000000 --- a/kubernetes-watch/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# kubernetes-watch-client - -Client for streaming events from watch enabled endpoints. - -## Example -Following is a simple example which -just streams to stdout. First some setup - this assumes kubernetes is accessible -at http://localhost:8001, e.g. after running `kubectl proxy`: - -```haskell -> import qualified Data.ByteString.Streaming.Char8 as Q - -> manager <- newManager defaultManagerSettings -> defaultConfig <- newConfig -> config = defaultConfig { configHost = "http://localhost:8001", configValidateAuthMethods = False } -> request = listEndpointsForAllNamespaces (Accept MimeJSON) -``` - -Launching 'dispatchWatch' with the above we get a stream of endpoints data: - -```haskell - > dispatchWatch manager config request Q.stdout - {"type":\"ADDED\","object":{"kind":\"Endpoints\","apiVersion":"v1","metadata":{"name":"heapster" .... -``` - -A more complex example involving some ggprocessing of the stream, the following -prints out the event types of each event. First, define functions to allow us apply -a parser to a stream: - - -```haskell -import Data.Aeson -import qualified Data.ByteString.Streaming.Char8 as Q -import Data.JsonStream.Parser -import qualified Streaming.Prelude as S - --- | Parse the stream using the given parser. -streamParse :: - FromJSON a => - Parser a - -> Q.ByteString IO r - -> Stream (Of [a]) IO r -streamParse parser byteStream = do - byteStream & Q.lines & parseEvent parser - --- | Parse a single event from the stream. -parseEvent :: - (FromJSON a, Monad m) => - Parser a - -> Stream (Q.ByteString m) m r - -> Stream (Of [a]) m r -parseEvent parser byteStream = S.map (parseByteString parser) (S.mapped Q.toStrict byteStream) -``` - -Next, define the parser and apply it to the stream: - -```haskell -> eventParser = value :: Parser (WatchEvent V1Endpoints) -> withResponseBody body = streamParse eventParser body & S.map (map eventType) -> dispatchWatch manager config request (S.print . withResponseBody) -[\"ADDED\"] -[\"ADDED\"] -[\"MODIFIED\"] -... -``` - -Packages in this example: - * Data.Aeson -- from [aeson](https://hackage.haskell.org/package/aeson) - * Data.ByteString.Streaming.Char8 from [streaming-bytestring](https://hackage.haskell.org/package/streaming-bytestring-0.1.5/docs/Data-ByteString-Streaming-Char8.html) - * Data.JsonStream.Parser from [json-stream](https://hackage.haskell.org/package/json-stream-0.4.1.5/docs/Data-JsonStream-Parser.html) - * Streaming.Prelude from [streaming](https://hackage.haskell.org/package/streaming-0.2.0.0/docs/Streaming-Prelude.html) diff --git a/kubernetes-watch/package.yaml b/kubernetes-watch/package.yaml deleted file mode 100644 index 68084de4..00000000 --- a/kubernetes-watch/package.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: kubernetes-watch -version: 0.1.0.0 -library: - source-dirs: src -dependencies: - - base >=4.7 && <5.0 - - aeson >=1.0 && <2.0 - - bytestring >=0.10.0 && <0.11 - - http-client >=0.5 && <0.6 - - mtl >=2.2.1 - - streaming-bytestring >= 0.1.5 && < 0.2.0 - - text >=0.11 && <1.3 - - kubernetes-openapi-client-gen == 0.1.0.0 - - diff --git a/kubernetes/README.md b/kubernetes/README.md index e3411cd5..592fdd08 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -8,4 +8,4 @@ OpenAPI-Specification: https://github.com/OAI/OpenAPI-Specification/blob/master/ ## Usage -Please refer to the README of the `kubernetes-client-helper` package. +Please refer to the README of the `kubernetes-client` package. diff --git a/stack.yaml b/stack.yaml index 9773dfad..4db55312 100644 --- a/stack.yaml +++ b/stack.yaml @@ -2,6 +2,4 @@ resolver: lts-10.0 extra-deps: packages: - kubernetes -- kubernetes-client-helper -- kubernetes-watch -- kubeconfig +- kubernetes-client