The only way we provide OS params to metadata daemon clients is
through a JSON file containing a lot of data. This patch exposes
individual parameters in a RESTful way, making it easier to actually
use the metadata daemon.

Signed-off-by: Hrvoje Ribicic <[email protected]>
---
 doc/design-os.rst             |  5 +++++
 src/Ganeti/Metad/Config.hs    |  9 ++++++---
 src/Ganeti/Metad/WebServer.hs | 31 +++++++++++++++++++++++++++++++
 3 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/doc/design-os.rst b/doc/design-os.rst
index 1ff09c0..d89d133 100644
--- a/doc/design-os.rst
+++ b/doc/design-os.rst
@@ -460,6 +460,11 @@ pair ``(<value>, <visibility>)`` as the value, where 
``<value>`` is the
 user-provided value of the parameter, and ``<visibility>`` is either 
``public``,
 ``private`` or ``secret``.
 
+The OS parameters can also be accessed individually by issuing a GET request
+to::
+
+  http://169.254.169.254/ganeti/<version>/os/parameters/<parameter>
+
 The installation scripts to be run inside the virtualized environment will be
 available at::
 
diff --git a/src/Ganeti/Metad/Config.hs b/src/Ganeti/Metad/Config.hs
index ebd27b2..3162243 100644
--- a/src/Ganeti/Metad/Config.hs
+++ b/src/Ganeti/Metad/Config.hs
@@ -80,9 +80,12 @@ getSecretOsParams = getOsParams "osparams_secret" "secret"
 -- > { "os-image": ["http://example.com/disk.img";, "public"],
 -- >   "os-password": ["mypassword", "secret"] }
 makeInstanceParams
-  :: JSObject JSValue -> JSObject JSValue -> JSObject JSValue -> JSValue
+  :: JSObject JSValue
+  -> JSObject JSValue
+  -> JSObject JSValue
+  -> JSObject JSValue
 makeInstanceParams pub priv sec =
-  JSObject . JSON.toJSObject $
+  JSON.toJSObject $
     addVisibility "public" pub ++
     addVisibility "private" priv ++
     addVisibility "secret" sec
@@ -92,7 +95,7 @@ makeInstanceParams pub priv sec =
     addVisibility param params =
       map (second (JSArray . (:[key param]))) (JSON.fromJSObject params)
 
-getOsParamsWithVisibility :: JSValue -> Result JSValue
+getOsParamsWithVisibility :: JSValue -> Result (JSObject JSValue)
 getOsParamsWithVisibility json =
   do obj <- readJSON json
      publicOsParams <- getPublicOsParams obj
diff --git a/src/Ganeti/Metad/WebServer.hs b/src/Ganeti/Metad/WebServer.hs
index 338d3e4..d47ab52 100644
--- a/src/Ganeti/Metad/WebServer.hs
+++ b/src/Ganeti/Metad/WebServer.hs
@@ -43,6 +43,7 @@ import qualified Control.Monad.CatchIO as CatchIO (catch)
 import qualified Data.CaseInsensitive as CI
 import Data.List (intercalate)
 import Data.Map (Map)
+import qualified Data.List as List
 import qualified Data.Map as Map
 import qualified Data.ByteString.Char8 as ByteString (pack, unpack)
 import Snap.Core
@@ -99,6 +100,19 @@ serveOsParams inst params =
        ByteString.pack .
        JSON.encode $ osParams
 
+lookupSingleParam :: String -> JSObject JSValue -> Result String
+lookupSingleParam param osParams =
+  case List.lookup param (JSON.fromJSObject osParams) of
+    Nothing -> Error $ "Instance does not have param " ++ param
+    Just v -> head <$> JSON.readJSON v
+
+serveSingleOsParam :: String -> Map String JSValue -> String -> MetaM
+serveSingleOsParam inst params param =
+  do instParams <- lookupInstanceParams inst params
+     maybeResult (Config.getOsParamsWithVisibility instParams >>=
+                  lookupSingleParam param) $ \paramValue ->
+       writeBS . ByteString.pack $ paramValue
+
 serveOsPackage :: String -> Map String JSValue -> String -> MetaM
 serveOsPackage inst params key =
   do instParams <- lookupInstanceParams inst params
@@ -164,6 +178,23 @@ handleMetadata params GET  "ganeti" "latest" 
"os/parameters.json" =
        \err -> do
          liftIO . Logging.logWarning $ "Could not serve OS parameters: " ++ err
          error404
+handleMetadata params GET  "ganeti" "latest" paramPath | isParamPath paramPath 
=
+  case split paramPath of
+    -- The validation of the first two entries is done in isParamPath
+    [_, _, param] -> do
+      remoteAddr <- ByteString.unpack . rqRemoteAddr <$> getRequest
+      instanceParams <- liftIO $ do
+        Logging.logInfo $ "OS package for " ++ show remoteAddr
+        readMVar params
+      serveSingleOsParam remoteAddr instanceParams param
+        `catchError`
+        \err -> do
+          liftIO .
+            Logging.logWarning $ "Could not serve single OS param " ++ param ++
+                                 ": " ++ err
+          error404
+    _ -> error404
+  where isParamPath = (==) ["os", "parameters"] . take 2 . split
 handleMetadata params GET  "ganeti" "latest" script | isScript script =
   do remoteAddr <- ByteString.unpack . rqRemoteAddr <$> getRequest
      instanceParams <- liftIO $ do
-- 
2.6.0.rc2.230.g3dd15c0

Reply via email to