Merge pull request #24 from Start9Labs/feature/app-endpoints

add routes for getting app config and manifest
This commit is contained in:
Lucy C
2020-10-08 10:22:18 -06:00
committed by GitHub
9 changed files with 113 additions and 11 deletions

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ agent_*
agent.* agent.*
version version
**/*.s9pk **/*.s9pk
**/appmgr

View File

@@ -1,6 +1,9 @@
/apps AppsManifestR GET -- get current apps listing /apps AppsManifestR GET -- get current apps listing
/apps/#S9PK/#Text/manifest AppManifestR GET -- get app manifest from appmgr
/apps/#S9PK/#Text/config AppConfigR GET -- get app config from appmgr
/version VersionR GET /version VersionR GET
/apps/version/#Text VersionAppR GET -- get most recent appId version /apps/version/#Text VersionAppR GET -- get most recent appId version
@@ -8,4 +11,5 @@
/icons/#PNG IconsR GET -- get icons /icons/#PNG IconsR GET -- get icons
!/apps/#S9PK AppR GET -- get most recent appId at appversion spec, defaults to >=0.0.0 -- ?spec={semver-spec} !/apps/#S9PK AppR GET -- get most recent appId at appversion spec, defaults to >=0.0.0 -- ?spec={semver-spec}
!/sys/#SYS_EXTENSIONLESS SysR GET -- get most recent sys app -- ?spec={semver-spec} !/sys/#SYS_EXTENSIONLESS SysR GET -- get most recent sys app -- ?spec={semver-spec}

View File

@@ -32,6 +32,7 @@ resources-path: "_env:RESOURCES_PATH:/var/www/html/resources"
ssl-path: "_env:SSL_PATH:/var/ssl" ssl-path: "_env:SSL_PATH:/var/ssl"
registry-hostname: "_env:REGISTRY_HOSTNAME:registry.start9labs.com" registry-hostname: "_env:REGISTRY_HOSTNAME:registry.start9labs.com"
tor-port: "_env:TOR_PORT:447" tor-port: "_env:TOR_PORT:447"
static-bin-dir: "_env:STATIC_BIN:/usr/local/bin/"
database: database:
database: "_env:PG_DATABASE:start9_registry" database: "_env:PG_DATABASE:start9_registry"

View File

@@ -2,13 +2,15 @@ name: start9-registry
version: 0.1.0 version: 0.1.0
default-extensions: default-extensions:
- NoImplicitPrelude - FlexibleInstances
- GeneralizedNewtypeDeriving - GeneralizedNewtypeDeriving
- LambdaCase - LambdaCase
- MultiWayIf - MultiWayIf
- NamedFieldPuns - NamedFieldPuns
- NoImplicitPrelude
- NumericUnderscores - NumericUnderscores
- OverloadedStrings - OverloadedStrings
- StandaloneDeriving
dependencies: dependencies:
- base >=4.12 && <5 - base >=4.12 && <5
@@ -34,10 +36,12 @@ dependencies:
- persistent-template - persistent-template
- process - process
- protolude - protolude
- shakespeare
- template-haskell - template-haskell
- text - text
- time - time
- transformers - transformers
- typed-process
- unordered-containers - unordered-containers
- unix - unix
- wai - wai

View File

@@ -40,6 +40,7 @@ import Lib.Types.AppIndex
import Lib.Types.Semver import Lib.Types.Semver
import Lib.Types.FileSystem import Lib.Types.FileSystem
import Lib.Error import Lib.Error
import Lib.External.AppMgr
import Settings import Settings
import Database.Queries import Database.Queries
import Network.Wai ( Request(requestHeaderUserAgent) ) import Network.Wai ( Request(requestHeaderUserAgent) )
@@ -85,6 +86,20 @@ getSysR e = do
sysResourceDir <- (</> "sys") . resourcesDir . appSettings <$> getYesod sysResourceDir <- (</> "sys") . resourcesDir . appSettings <$> getYesod
getApp sysResourceDir e getApp sysResourceDir e
getAppManifestR :: Extension "s9pk" -> Text -> Handler TypedContent
getAppManifestR e@(Extension appId) v = do
appMgrDir <- (<> "/") . staticBinDir . appSettings <$> getYesod
appDir <- (<> "/") . (</> toS v) . (</> appId) . (</> "apps") . resourcesDir . appSettings <$> getYesod
manifest <- handleS9ErrT $ getManifest appMgrDir appDir e
pure $ TypedContent "application/json" (toContent manifest)
getAppConfigR :: Extension "s9pk" -> Text -> Handler TypedContent
getAppConfigR e@(Extension appId) v = do
appMgrDir <- (<> "/") . staticBinDir . appSettings <$> getYesod
appDir <- (<> "/") . (</> toS v) . (</> appId) . (</> "apps") . resourcesDir . appSettings <$> getYesod
config <- handleS9ErrT $ getConfig appMgrDir appDir e
pure $ TypedContent "application/json" (toContent config)
getAppR :: Extension "s9pk" -> Handler TypedContent getAppR :: Extension "s9pk" -> Handler TypedContent
getAppR e = do getAppR e = do
appResourceDir <- (</> "apps") . resourcesDir . appSettings <$> getYesod appResourceDir <- (</> "apps") . resourcesDir . appSettings <$> getYesod

View File

@@ -1,14 +1,19 @@
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE QuasiQuotes #-}
module Lib.Error where module Lib.Error where
import Startlude import Startlude
import Network.HTTP.Types import Network.HTTP.Types
import Yesod.Core import Yesod.Core
import Data.String.Interpolate.IsString
type S9ErrT m = ExceptT S9Error m type S9ErrT m = ExceptT S9Error m
data S9Error = PersistentE Text data S9Error =
PersistentE Text
| AppMgrE Text Int
deriving (Show, Eq) deriving (Show, Eq)
instance Exception S9Error instance Exception S9Error
@@ -17,8 +22,12 @@ instance Exception S9Error
toError :: S9Error -> Error toError :: S9Error -> Error
toError = \case toError = \case
PersistentE t -> Error DATABASE_ERROR t PersistentE t -> Error DATABASE_ERROR t
AppMgrE cmd code -> Error APPMGR_ERROR [i|"appmgr #{cmd}" exited with #{code}|]
data ErrorCode =
DATABASE_ERROR
| APPMGR_ERROR
data ErrorCode = DATABASE_ERROR
deriving (Eq, Show) deriving (Eq, Show)
instance ToJSON ErrorCode where instance ToJSON ErrorCode where
toJSON = String . show toJSON = String . show
@@ -43,6 +52,8 @@ instance ToContent S9Error where
toStatus :: S9Error -> Status toStatus :: S9Error -> Status
toStatus = \case toStatus = \case
PersistentE _ -> status500 PersistentE _ -> status500
AppMgrE _ _ -> status500
handleS9ErrT :: MonadHandler m => S9ErrT m a -> m a handleS9ErrT :: MonadHandler m => S9ErrT m a -> m a
handleS9ErrT action = runExceptT action >>= \case handleS9ErrT action = runExceptT action >>= \case

57
src/Lib/External/AppMgr.hs vendored Normal file
View File

@@ -0,0 +1,57 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Lib.External.AppMgr where
import Startlude
import qualified Data.ByteString.Lazy as LBS
import Data.String.Interpolate.IsString
import System.Process.Typed hiding ( createPipe )
import Lib.Error
import Lib.Registry
readProcessWithExitCode' :: MonadIO m => String -> [String] -> ByteString -> m (ExitCode, ByteString, ByteString)
readProcessWithExitCode' a b c = liftIO $ do
let pc =
setStdin (byteStringInput $ LBS.fromStrict c)
$ setStderr byteStringOutput
$ setEnvInherit
$ setStdout byteStringOutput
$ (System.Process.Typed.proc a b)
withProcessWait pc $ \process -> atomically $ liftA3 (,,)
(waitExitCodeSTM process)
(fmap LBS.toStrict $ getStdout process)
(fmap LBS.toStrict $ getStderr process)
readProcessInheritStderr :: MonadIO m => String -> [String] -> ByteString -> m (ExitCode, ByteString)
readProcessInheritStderr a b c = liftIO $ do
let pc =
setStdin (byteStringInput $ LBS.fromStrict c)
$ setStderr inherit
$ setEnvInherit
$ setStdout byteStringOutput
$ (System.Process.Typed.proc a b)
withProcessWait pc
$ \process -> atomically $ liftA2 (,) (waitExitCodeSTM process) (fmap LBS.toStrict $ getStdout process)
getConfig :: (MonadIO m, KnownSymbol a) => FilePath -> FilePath -> Extension a -> S9ErrT m Text
getConfig appmgrPath appPath e@(Extension appId) = fmap decodeUtf8 $ do
(ec, out) <- readProcessInheritStderr (appmgrPath <> "appmgr") ["inspect", "info", appPath <> show e, "-C", "--json"] ""
case ec of
ExitSuccess -> pure out
ExitFailure n -> throwE $ AppMgrE [i|info #{appId} -C \--json|] n
getManifest :: (MonadIO m, KnownSymbol a) => FilePath -> FilePath -> Extension a -> S9ErrT m ByteString
getManifest appmgrPath appPath e@(Extension appId) = do
(ec, bs) <- readProcessInheritStderr (appmgrPath <> "appmgr") ["inspect", "info", appPath <> show e, "-M", "--json"] ""
case ec of
ExitSuccess -> pure bs
ExitFailure n -> throwE $ AppMgrE [i|info -M #{appId} \--json|] n

View File

@@ -50,6 +50,7 @@ data AppSettings = AppSettings
, sslCsrLocation :: FilePath , sslCsrLocation :: FilePath
, sslCertLocation :: FilePath , sslCertLocation :: FilePath
, torPort :: AppPort , torPort :: AppPort
, staticBinDir :: FilePath
} }
instance FromJSON AppSettings where instance FromJSON AppSettings where
@@ -64,6 +65,7 @@ instance FromJSON AppSettings where
sslPath <- o .: "ssl-path" sslPath <- o .: "ssl-path"
registryHostname <- o .: "registry-hostname" registryHostname <- o .: "registry-hostname"
torPort <- o .: "tor-port" torPort <- o .: "tor-port"
staticBinDir <- o .: "static-bin-dir"
let sslKeyLocation = sslPath </> "key.pem" let sslKeyLocation = sslPath </> "key.pem"
let sslCsrLocation = sslPath </> "certificate.csr" let sslCsrLocation = sslPath </> "certificate.csr"

View File

@@ -72,3 +72,10 @@ spec = do
statusIs 200 statusIs 200
apps <- runDBtest $ selectList ([] :: [Filter SApp])[] apps <- runDBtest $ selectList ([] :: [Filter SApp])[]
assertEq "no apps should exist" (length apps) 0 assertEq "no apps should exist" (length apps) 0
describe "GET /apps/#S9PK/#Text/manifest" $
withApp $ it "gets bitcoin manifest" $ do
request $ do
setMethod "GET"
setUrl ("/apps/bitcoind.s9pk/0.2.5/manifest" :: Text)
statusIs 200
bodyContains "{\"id\":\"bitcoind\",\"version\":\"0.20.1\",\"title\":\"Bitcoin Core\",\"description\":{\"short\":\"Bitcoin Full Node by Bitcoin Core\",\"long\":\"Bitcoin is an innovative payment network and a new kind of money. Bitcoin uses peer-to-peer technology to operate with no central authority or banks; managing transactions and the issuing of bitcoins is carried out collectively by the network. Bitcoin is open-source; its design is public, nobody owns or controls Bitcoin and everyone can take part. Through many of its unique properties, Bitcoin allows exciting uses that could not be covered by any previous payment system.\"},\"release-notes\":\"https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.20.1.md\",\"has-instructions\":true,\"os-version-required\":\">=0.2.4\",\"os-version-recommended\":\">=0.2.4\",\"ports\":[{\"internal\":8332,\"tor\":8332},{\"internal\":8333,\"tor\":8333}],\"image\":{\"type\":\"tar\"},\"mount\":\"/root/.bitcoin\",\"assets\":[{\"src\":\"bitcoin.conf.template\",\"dst\":\".\",\"overwrite\":true}],\"hidden-service-version\":\"v2\",\"dependencies\":{}}"