Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make alertmanager client compile with ghc9 #3

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Alertmanager

## 0.1.0

+ Initial generation
+ Make it compile with ghc 9
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ space := $(null) #
comma := ,

generate:
openapi-generator generate \
openapi-generator-cli generate \
-i alertmanager/api/v2/openapi.yaml \
--additional-properties $(subst $(space),$(comma),$(strip $(PROPERTIES))) \
--ignore-file-override alertmanager-openapi/.openapi-generator-ignore \
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
# Alertmanager Client library

This is a WIP library for using the Alertmanager HTTP API for creating & modifying alerts from Haskell code.
This is a library for using the Alertmanager HTTP API for creating & modifying alerts from Haskell code.

It is split into two parts:
* `alertmanager-openapi` - a Haskell library which contains auto-generated bindings for the Alertmanager v2 HTTP API, generated using `openapi-generator` from the Alertmanager API Swagger file in their repository (pulled in via submodule).
* `alertmanager-client` - an `amazonka`-inspired Haskell library which provides a high-level interface, including cleaner types and a monad transformer, for running queries against the Alertmanager v2 HTTP API.


# Usage

run `git submodule update --recursive` then enter the nix shell.
Follow the steps below.


## Generating the OpenAPI bindings


A command to re-generate the bindings is provided in the top-level `Makefile` - simply run `make generate`.

Note that some files normally generated by the tool have to be modified by hand. For example, the `.cabal` file contains copyright information, so the tool is forbidden from overwriting it. The full list of such files is contained in `.openapi-generator-ignore`.
Expand Down
4 changes: 2 additions & 2 deletions alertmanager-client/alertmanager-client.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ library

build-depends: base >= 4.7 && < 5
, alertmanager-openapi >= 0.0.1 && < 0.0.2
, http-client >= 0.5 && < 0.7
, lens >= 4 && < 5
, http-client >= 0.5 && < 0.8
, lens >= 4 && < 6
, text >= 0.11 && < 2
, transformers >= 0.5 && < 0.6
default-language: Haskell2010
Expand Down
35 changes: 35 additions & 0 deletions alertmanager-client/shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{ nixpkgs ? import ../nix/pin.nix {}, compiler ? "default", doBenchmark ? false }:

let

inherit (nixpkgs) pkgs;

f = { mkDerivation, alertmanager-openapi, base, http-client, lens
, lib, text, transformers, cabal-install
}:
mkDerivation {
pname = "alertmanager-client";
version = "0.1.0.0";
src = ./.;
libraryToolDepends = [ cabal-install ];
libraryHaskellDepends = [
alertmanager-openapi base http-client lens text transformers
];
homepage = "https://github.com/SupercedeTech/alertmanager-client#readme";
license = lib.licenses.bsd3;
};
Comment on lines +7 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use callCabal2Nix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because many, /many/ more packages are broken, I considered going from me not being able to build it at all to having it generate and build the new bindings good enough for this upgrade cycle.


haskellPackages = (if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler}).extend(self: super: {
alertmanager-openapi = haskellPackages.callPackage ../alertmanager-openapi {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is explicitly adding the package necessary here but not above?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not available in the package set. no-one uploaded it to hackage.
Even if it was you want to override it to the current version in the tree rather then the one on hackage because it may contain changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my question wasn't clear - why isn't it necessary to add alertmanager-openapi to the package set when the compiler choice is "default"? It won't be in the Hackage set in either case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise cabal can't find it, nix patches cabal somehow

});

variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;

drv = variant (haskellPackages.callPackage f {
});

in

if pkgs.lib.inNixShell then drv.env else drv
2 changes: 1 addition & 1 deletion alertmanager-openapi/.openapi-generator/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.2.1-SNAPSHOT
5.4.0
23 changes: 12 additions & 11 deletions alertmanager-openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ stack haddock
```
which will generate docs for this lib in the `docs` folder.

To generate the docs in the normal location (to enable hyperlinks to external libs), remove
To generate the docs in the normal location (to enable hyperlinks to external libs), remove
```
build:
haddock-arguments:
Expand Down Expand Up @@ -55,7 +55,7 @@ These options allow some customization of the code generation process.
**haskell-http-client additional properties:**

| OPTION | DESCRIPTION | DEFAULT | ACTUAL |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------- |
|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------|
| allowFromJsonNulls | allow JSON Null during model decoding from JSON | true | true |
| allowNonUniqueOperationIds | allow *different* API modules to contain the same operationId. Each API must be imported qualified | false | false |
| allowToJsonNulls | allow emitting JSON Null during model encoding to JSON | false | false |
Expand All @@ -76,13 +76,14 @@ These options allow some customization of the code generation process.
| requestType | Set the name of the type used to generate requests | | AlertmanagerRequest |
| strictFields | Add strictness annotations to all model fields | true | true |
| useKatip | Sets the default value for the UseKatip cabal flag. If true, the katip package provides logging instead of monad-logger | true | false |
| queryExtraUnreserved | Configures additional querystring characters which must not be URI encoded, e.g. '+' or ':' | | |

[1]: https://www.stackage.org/haddock/lts-9.0/iso8601-time-0.1.4/Data-Time-ISO8601.html#v:formatISO8601Millis

An example setting _dateTimeFormat_ and _strictFields_:

```
java -jar openapi-generator-cli.jar generate -i petstore.yaml -g haskell-http-client -o output/haskell-http-client --additional-properties=dateTimeFormat="%Y-%m-%dT%H:%M:%S%Q%z" --additional-properties=strictFields=false
java -jar openapi-generator-cli.jar generate -i petstore.yaml -g haskell-http-client -o output/haskell-http-client --additional-properties=dateTimeFormat="%Y-%m-%dT%H:%M:%S%Q%z" --additional-properties=strictFields=false
```

View the full list of Codegen "config option" parameters with the command:
Expand Down Expand Up @@ -112,7 +113,7 @@ This library is intended to be imported qualified.
| MODULE | NOTES |
| ------------------- | --------------------------------------------------- |
| Network.Alertmanager.OpenAPI.Client | use the "dispatch" functions to send requests |
| Network.Alertmanager.OpenAPI.Core | core funcions, config and request types |
| Network.Alertmanager.OpenAPI.Core | core functions, config and request types |
| Network.Alertmanager.OpenAPI.API | construct api requests |
| Network.Alertmanager.OpenAPI.Model | describes api models |
| Network.Alertmanager.OpenAPI.MimeTypes | encoding/decoding MIME types (content-types/accept) |
Expand All @@ -136,10 +137,10 @@ in GHCi or via the Haddocks.
* optional non-body parameters are included by using `applyOptionalParam`
* optional body parameters are set by using `setBodyParam`

Example code generated for pretend _addFoo_ operation:
Example code generated for pretend _addFoo_ operation:

```haskell
data AddFoo
data AddFoo
instance Consumes AddFoo MimeJSON
instance Produces AddFoo MimeJSON
instance Produces AddFoo MimeXML
Expand Down Expand Up @@ -182,14 +183,14 @@ the config, it will be applied to the request.

```haskell
mgr <- newManager defaultManagerSettings
config0 <- withStdoutLogging =<< newConfig
config0 <- withStdoutLogging =<< newConfig
let config = config0
`addAuthMethod` AuthOAuthFoo "secret-key"

let addFooRequest =
addFoo
(ContentType MimeJSON)
(Accept MimeXML)
let addFooRequest =
addFoo
(ContentType MimeJSON)
(Accept MimeXML)
(ParamBar paramBar)
(ParamQux paramQux)
modelBaz
Expand Down
6 changes: 3 additions & 3 deletions alertmanager-openapi/alertmanager-openapi.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ library
lib
ghc-options: -Wall -funbox-strict-fields
build-depends:
aeson >=1.0 && <2.0
aeson >=2.0 && <3.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying that this big version bump is necessary. I wish the OpenAPI people had supported multiple aeson versions, but it seems that the Aeson maintainers aren't supporting the 1.* releases anymore.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aeson 1.xx is susceptible to DoS attacks. It's possible to create a universal hash collision for FNV based hash algorithms.

, base >=4.7 && <5.0
, base64-bytestring >1.0 && <2.0
, bytestring >=0.10.0 && <0.11
, case-insensitive
, containers >=0.5.0.0 && <0.8
, deepseq >= 1.4 && <1.6
, exceptions >= 0.4
, http-api-data >= 0.3.4 && <0.5
, http-client >=0.5 && <0.7
, http-api-data >= 0.3.4 && <0.6
, http-client >=0.5 && <0.8
, http-client-tls
, http-media >= 0.4 && < 0.9
, http-types >=0.8 && <0.13
Expand Down
27 changes: 27 additions & 0 deletions alertmanager-openapi/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{ mkDerivation, aeson, base, base64-bytestring, bytestring
, case-insensitive, containers, deepseq, exceptions, hspec
, http-api-data, http-client, http-client-tls, http-media
, http-types, iso8601-time, lib, microlens, monad-logger, mtl
, network, QuickCheck, random, safe-exceptions, semigroups, text
, time, transformers, unordered-containers, vector, cabal-install
}:
mkDerivation {
pname = "alertmanager-openapi";
version = "0.0.1.0";
src = ./.;
libraryToolDepends = [cabal-install];
libraryHaskellDepends = [
aeson base base64-bytestring bytestring case-insensitive containers
deepseq exceptions http-api-data http-client http-client-tls
http-media http-types iso8601-time microlens monad-logger mtl
network random safe-exceptions text time transformers
unordered-containers vector
];
testHaskellDepends = [
aeson base bytestring containers hspec iso8601-time mtl QuickCheck
semigroups text time transformers unordered-containers vector
];
homepage = "https://openapi-generator.tech";
description = "Auto-generated alertmanager-openapi API Client";
license = lib.licenses.bsd3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import qualified Prelude as P
--
-- Get a list of alerts
--
getAlerts
getAlerts
:: AlertmanagerRequest GetAlerts MimeNoContent [GettableAlert] MimeJSON
getAlerts =
_mkRequest "GET" ["/alerts"]
Expand Down Expand Up @@ -109,7 +109,7 @@ instance Produces GetAlerts MimeJSON
--
-- Create new Alerts
--
postAlerts
postAlerts
:: (Consumes PostAlerts MimeJSON, MimeRender MimeJSON Alerts)
=> Alerts -- ^ "alerts" - The alerts to create
-> AlertmanagerRequest PostAlerts MimeJSON NoContent MimeNoContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import qualified Prelude as P
--
-- Get a list of alert groups
--
getAlertGroups
getAlertGroups
:: AlertmanagerRequest GetAlertGroups MimeNoContent [AlertGroup] MimeJSON
getAlertGroups =
_mkRequest "GET" ["/alerts/groups"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import qualified Prelude as P
--
-- Get current status of an Alertmanager instance and its cluster
--
getStatus
getStatus
:: AlertmanagerRequest GetStatus MimeNoContent AlertmanagerStatus MimeJSON
getStatus =
_mkRequest "GET" ["/status"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import qualified Prelude as P
--
-- Get list of all receivers (name of notification integrations)
--
getReceivers
getReceivers
:: AlertmanagerRequest GetReceivers MimeNoContent [Receiver] MimeJSON
getReceivers =
_mkRequest "GET" ["/receivers"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import qualified Prelude as P
--
-- Delete a silence by its ID
--
deleteSilence
deleteSilence
:: SilenceId -- ^ "silenceId" - ID of the silence to get
-> AlertmanagerRequest DeleteSilence MimeNoContent NoContent MimeNoContent
deleteSilence (SilenceId silenceId) =
Expand All @@ -79,7 +79,7 @@ instance Produces DeleteSilence MimeNoContent
--
-- Get a silence by its ID
--
getSilence
getSilence
:: SilenceId -- ^ "silenceId" - ID of the silence to get
-> AlertmanagerRequest GetSilence MimeNoContent GettableSilence MimeJSON
getSilence (SilenceId silenceId) =
Expand All @@ -96,7 +96,7 @@ instance Produces GetSilence MimeJSON
--
-- Get a list of silences
--
getSilences
getSilences
:: AlertmanagerRequest GetSilences MimeNoContent [GettableSilence] MimeJSON
getSilences =
_mkRequest "GET" ["/silences"]
Expand All @@ -117,7 +117,7 @@ instance Produces GetSilences MimeJSON
--
-- Post a new silence or update an existing one
--
postSilences
postSilences
:: (Consumes PostSilences MimeJSON, MimeRender MimeJSON PostableSilence)
=> PostableSilence -- ^ "silence" - The silence to create
-> AlertmanagerRequest PostSilences MimeJSON InlineResponse200 MimeJSON
Expand Down
28 changes: 17 additions & 11 deletions alertmanager-openapi/lib/Network/Alertmanager/OpenAPI/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import qualified Control.Exception.Safe as E
import qualified Control.Monad.IO.Class as P
import qualified Control.Monad as P
import qualified Data.Aeson.Types as A
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BCL
Expand Down Expand Up @@ -69,16 +70,16 @@ dispatchLbs manager config request = do
-- | pair of decoded http body and http response
data MimeResult res =
MimeResult { mimeResult :: Either MimeError res -- ^ decoded http body
, mimeResultResponse :: NH.Response BCL.ByteString -- ^ http response
, mimeResultResponse :: NH.Response BCL.ByteString -- ^ http response
}
deriving (Show, Functor, Foldable, Traversable)

-- | pair of unrender/parser error and http response
data MimeError =
MimeError {
mimeError :: String -- ^ unrender/parser error
, mimeErrorResponse :: NH.Response BCL.ByteString -- ^ http response
} deriving (Eq, Show)
, mimeErrorResponse :: NH.Response BCL.ByteString -- ^ http response
} deriving (Show)

-- | send a request returning the 'MimeResult'
dispatchMime
Expand Down Expand Up @@ -171,21 +172,26 @@ _toInitRequest
=> AlertmanagerClientConfig -- ^ config
-> AlertmanagerRequest req contentType res accept -- ^ request
-> IO (InitRequest req contentType res accept) -- ^ initialized request
_toInitRequest config req0 =
_toInitRequest config req0 =
runConfigLogWithExceptions "Client" config $ do
parsedReq <- P.liftIO $ NH.parseRequest $ BCL.unpack $ BCL.append (configHost config) (BCL.concat (rUrlPath req0))
req1 <- P.liftIO $ _applyAuthMethods req0 config
P.when
(configValidateAuthMethods config && (not . null . rAuthTypes) req1)
(E.throw $ AuthMethodException $ "AuthMethod not configured: " <> (show . head . rAuthTypes) req1)
let req2 = req1 & _setContentTypeHeader & _setAcceptHeader
reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders (rParams req2)
reqQuery = NH.renderQuery True (paramsQuery (rParams req2))
pReq = parsedReq { NH.method = (rMethod req2)
params = rParams req2
reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders params
reqQuery = let query = paramsQuery params
queryExtraUnreserved = configQueryExtraUnreserved config
in if B.null queryExtraUnreserved
then NH.renderQuery True query
else NH.renderQueryPartialEscape True (toPartialEscapeQuery queryExtraUnreserved query)
pReq = parsedReq { NH.method = rMethod req2
, NH.requestHeaders = reqHeaders
, NH.queryString = reqQuery
}
outReq <- case paramsBody (rParams req2) of
outReq <- case paramsBody params of
ParamBodyNone -> pure (pReq { NH.requestBody = mempty })
ParamBodyB bs -> pure (pReq { NH.requestBody = NH.RequestBodyBS bs })
ParamBodyBL bl -> pure (pReq { NH.requestBody = NH.RequestBodyLBS bl })
Expand All @@ -202,16 +208,16 @@ modifyInitRequest (InitRequest req) f = InitRequest (f req)
modifyInitRequestM :: Monad m => InitRequest req contentType res accept -> (NH.Request -> m NH.Request) -> m (InitRequest req contentType res accept)
modifyInitRequestM (InitRequest req) f = fmap InitRequest (f req)

-- ** Logging
-- ** Logging

-- | Run a block using the configured logger instance
runConfigLog
:: P.MonadIO m
=> AlertmanagerClientConfig -> LogExec m
=> AlertmanagerClientConfig -> LogExec m a
runConfigLog config = configLogExecWithContext config (configLogContext config)

-- | Run a block using the configured logger instance (logs exceptions)
runConfigLogWithExceptions
:: (E.MonadCatch m, P.MonadIO m)
=> T.Text -> AlertmanagerClientConfig -> LogExec m
=> T.Text -> AlertmanagerClientConfig -> LogExec m a
runConfigLogWithExceptions src config = runConfigLog config . logExceptions src
Loading