Skip to content

Commit

Permalink
Merge pull request #13 from Nike-Inc/feature/config-management
Browse files Browse the repository at this point in the history
Add configuration download and upload for jobs
  • Loading branch information
dogonthehorizon authored Nov 17, 2016
2 parents e7e71a0 + 18e171e commit 0c925c7
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ cabal.sandbox.config
.stack-work/
cabal.project.local
.HTF/
ptl-pthread
7 changes: 7 additions & 0 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Bartlett.Types
import qualified Bartlett.Configuration as C
import qualified Bartlett.Actions.Info as AI
import qualified Bartlett.Actions.Build as AB
import qualified Bartlett.Actions.Config as AC
import qualified Bartlett.Parsers as P

import Control.Exception
Expand Down Expand Up @@ -74,6 +75,12 @@ executeCommand cmd usr jenkinsInstance =
AI.getInfo usr jenkinsInstance jobPaths
Build jobPath jobParameters ->
AB.postBuild usr jenkinsInstance jobPath jobParameters
Config jobPath configFilePath ->
case configFilePath of
Just cp ->
AC.updateConfig usr jenkinsInstance jobPath cp
Nothing ->
AC.getConfig usr jenkinsInstance jobPath

-- | Execute the appropriate sub-command given parsed cli options.
run :: Options -> IO ()
Expand Down
5 changes: 3 additions & 2 deletions bartlett.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: bartlett
version: 1.0.1
version: 1.1.0
synopsis: The Jenkins command-line tool to serve your needs.
description: Please see README.md
homepage: https://github.com/Nike-inc/bartlett
Expand All @@ -20,7 +20,8 @@ library
Bartlett.Network,
Bartlett.Configuration,
Bartlett.Actions.Info,
Bartlett.Actions.Build
Bartlett.Actions.Build,
Bartlett.Actions.Config
ghc-options:
-fwarn-tabs
-fwarn-unused-imports
Expand Down
2 changes: 1 addition & 1 deletion src/Bartlett/Actions/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ postBuild ::
-> Maybe JobParameters -- ^ Optional set of job parameters to trigger with.
-> IO ()
postBuild user base path parameters = do
resp <- execRequest "post" reqOpts reqUri
resp <- execRequest "post" reqOpts reqUri Nothing
BL.putStrLn . encodePretty . BU.toResponseStatus $
resp ^. responseStatus
where (suffix, buildOpts) = consBuildType parameters
Expand Down
49 changes: 49 additions & 0 deletions src/Bartlett/Actions/Config.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{-|
Module : Config
Description : Methods for executing config requests against Jenkins
Copyright : (c) Nike, Inc., 2016
License : BSD3
Maintainer : fernando.freire@nike.com
Stability : stable
Methods for executing config requests against Jenkins.
-}
module Bartlett.Actions.Config where

import Bartlett.Network (execRequest)
import Bartlett.Types
import Bartlett.Util (toResponseStatus, mkUrl)

import Control.Lens ((^.), (?~), (&))
import Data.Aeson.Encode.Pretty (encodePretty)
import qualified Data.ByteString.Lazy.Char8 as BL
import Network.Wreq (responseStatus, responseBody, defaults, auth)

-- | Construct a URL to interact with Job configurations.
configUri :: JenkinsInstance -> JobPath -> BL.ByteString
configUri base path =
mkUrl base path "/config.xml"

-- | Retrieve the XML configuration for the given job.
getConfig :: BasicAuthUser a =>
a -- The user to authenticate with.
-> JenkinsInstance -- The Jenkins instance to interact with.
-> JobPath -- The Job for the given Jenkins instance to interact with.
-> IO () -- The XML configuration for the given job.
getConfig user base path = do
resp <- execRequest "get" reqOpts (configUri base path) Nothing
BL.putStrLn $ resp ^. responseBody
where reqOpts = defaults & auth ?~ getBasicAuth user

-- | Update the XML configuration for the given job.
updateConfig :: BasicAuthUser a =>
a -- The user to authenticate with.
-> JenkinsInstance -- The Jenkins instance to interact with.
-> JobPath -- The Job for the given Jenkins instance to interact with.
-> ConfigPath -- Path to the XML configuration to upload to Jenkins.
-> IO ()
updateConfig user base path configPath = do
configFile <- BL.readFile configPath
resp <- execRequest "post" reqOpts (configUri base path) (Just configFile)
BL.putStrLn . encodePretty . toResponseStatus $ resp ^. responseStatus
where reqOpts = defaults & auth ?~ getBasicAuth user
2 changes: 1 addition & 1 deletion src/Bartlett/Actions/Info.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ getInfo ::
-> IO ()
getInfo user base [] = return ()
getInfo user base (path:paths) = do
resp <- execRequest "get" reqOpts reqUri
resp <- execRequest "get" reqOpts reqUri Nothing
BL.putStrLn . toPrettyJson $ resp ^. responseBody
getInfo user base paths
where reqOpts = defaults & auth ?~ getBasicAuth user
Expand Down
34 changes: 21 additions & 13 deletions src/Bartlett/Network.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,36 @@ import Bartlett.Util (toResponseStatus, withForcedSSL)
import qualified Control.Exception as E
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.ByteString.Lazy.Char8 (ByteString, unpack)
import Data.Maybe (fromMaybe)
import qualified Network.HTTP.Client as NHC
import System.Exit (die)
import Network.Wreq (Options, Response, customMethodWith)
import Network.Wreq (Options, Response)
import qualified Network.Wreq.Session as S

-- | General request handler that provides basic error handling.
execRequest ::
ByteString -- ^ The type of request to make (e.g. "get")
-> Options -- ^ Request params to pass along with the request.
-> ByteString -- ^ The uri to make the request to
-> Maybe ByteString -- ^ The file to upload to the Jenkins instance.
-> IO (Response ByteString)
execRequest requestType opts reqUrl =
case requestType of
"post" ->
mkRequest "post" reqUrl
`E.catch`
recoverableErrorHandler (mkRequest "post" $ withForcedSSL reqUrl)
"get" ->
mkRequest "get" reqUrl
`E.catch`
recoverableErrorHandler (mkRequest "get" $ withForcedSSL reqUrl)

where mkRequest method url = customMethodWith method opts (unpack url)
execRequest requestType opts reqUrl postBody =
S.withAPISession $ \session ->
case requestType of
-- TODO Need to get a CSRF crumb
-- JENKINS_URL/crumbIssuer/api/json?xpath=?xpath=concat(//crumbRequestField,":",//crumb)')
-- TODO create a proper sum type for requestType
"post" ->
postSession reqUrl
`E.catch`
recoverableErrorHandler (postSession $ withForcedSSL reqUrl)
where fileToUpload = fromMaybe "" postBody :: ByteString
postSession url = S.postWith opts session (unpack url) fileToUpload
"get" ->
getSession reqUrl
`E.catch`
recoverableErrorHandler (getSession $ withForcedSSL reqUrl)
where getSession url = S.getWith opts session (unpack url)


-- | Handler that returns a JSON representation of the error status.
Expand Down
13 changes: 13 additions & 0 deletions src/Bartlett/Parsers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ parseJobParameters = option readerByteString $
short 'o' <> long "options" <> metavar "OPTIONS" <>
help "Comma separated list of key=value pairs to pass to the job"

-- | Parse a path to the config file to update.
parseConfigFilePath :: Parser ConfigPath
parseConfigFilePath = option str $
short 'f' <> long "filepath" <> metavar "CONFIG_FILE_PATH" <>
help "Path to the job configuration to upload"

-- | Parse an Info sub-command.
parseInfo :: Parser Command
parseInfo = Info <$> some (argument readerByteString (metavar "JOB_PATHS..."))
Expand All @@ -64,11 +70,18 @@ parseBuild = Build
<$> argument readerByteString (metavar "JOB_PATH")
<*> optional parseJobParameters

-- | Parse a Config sub-command.
parseConfig :: Parser Command
parseConfig = Config
<$> argument readerByteString (metavar "JOB_PATH")
<*> optional parseConfigFilePath

-- | Parse a Command.
parseCommand :: Parser Command
parseCommand = subparser $
command "info" (parseInfo `withInfo` "Get information on the given job")
<> command "build" (parseBuild `withInfo` "Trigger a build for the given job")
<> command "config" (parseConfig `withInfo` "Manage XML configurations for jobs")

-- | Combinator for all command line options.
parseOptions :: Parser Options
Expand Down
3 changes: 3 additions & 0 deletions src/Bartlett/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type JobParameters = ByteString
-- ^ Comma-separated list of key=value pairs to pass along to the triggered job.
type Profile = ByteString
-- ^ The profile to use when authenticating against Jenkins.
type ConfigPath = FilePath
-- ^ The path to the job configuration to upload.

-- | Defines methods for basic authentication
class BasicAuthUser a where
Expand All @@ -49,6 +51,7 @@ instance BasicAuthUser User where
data Command =
Info [JobPath] -- ^ Retrieve information for the given job.
| Build JobPath (Maybe JobParameters) -- ^ Build the given job with the given options.
| Config JobPath (Maybe ConfigPath) -- ^ Retrieve and upload job configurations.

-- | Represents all available CLI options for 'Bartlett'.
data Options =
Expand Down

0 comments on commit 0c925c7

Please sign in to comment.