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

Add support for YAML configuration files #6

Merged
merged 4 commits into from
Dec 5, 2024
Merged
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
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ normally support App Connectors.

## Example

Here is a sample config file, in JSON format:
Create a configuration file in either JSON or YAML format. Here’s an example:

`config.json`
```json
{
"routes": [
Expand All @@ -50,6 +51,19 @@ Here is a sample config file, in JSON format:
]
}
```
or `config.yaml`
```yaml
routes:
- "172.16.0.0/22"
- "192.168.0.0/24"
hostRoutes:
- "special-hostname1.example"
- "special-hostname2.example"
awsManagedPrefixLists:
- "pl-02761f4a40454a3c9"
extraArgs:
- "--webclient"
```

Run tailscale-manager:

Expand Down
11 changes: 5 additions & 6 deletions src/TailscaleManager.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ helpText = [r|Tailscale routes manager
Dynamically resolves a list of hostRoutes to IP addresses, then tells tailscale
to advertise them as /32 routes along with any normal CIDR routes.

Config file example:
Config file example (JSON):

{
"routes": [
Expand All @@ -49,7 +49,7 @@ Config file example:
],
"hostRoutes": [
"special-hostname1.example",
"special-hostname2.example",
"special-hostname2.example"
],
"awsManagedPrefixLists": [
"pl-02761f4a40454a3c9"
Expand All @@ -60,7 +60,7 @@ Config file example:
tsManagerOptions :: Parser TailscaleManagerOptions
tsManagerOptions =
TailscaleManagerOptions
<$> argument str (metavar "<configfile.json>")
<$> argument str (metavar "<configfile>")
<*> switch (long "dryrun"
<> help "Dryrun mode")
<*> strOption (long "tailscale"
Expand Down Expand Up @@ -137,7 +137,7 @@ runOnce options prevRoutes = do
logDelay
return prevRoutes

-- |Emit a log message describing the difference between old and new route sets.
-- | Emit a log message describing the difference between old and new route sets.
logDiff :: Set IPRange -> Set IPRange -> IO ()
logDiff prevRoutes newRoutes = do
logger <- myLogger
Expand All @@ -164,8 +164,7 @@ shrinkRatio :: Foldable t
-> Double -- ^ Shrink ratio
shrinkRatio old new = 1 - (1 / (fromIntegral (length old) / fromIntegral (length new)))

-- |Do all the hostname resolution and concat the results with static routes from
-- the config.
-- | Generate routes based on config, resolving hostnames and AWS-managed prefix lists.
generateRoutes :: TSConfig -> IO (Set IPRange)
generateRoutes config = do
hostRoutes <- resolveHostnamesToRoutes (tsHostRoutes config)
Expand Down
31 changes: 27 additions & 4 deletions src/TailscaleManager/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
module TailscaleManager.Config where

import Data.Aeson
import Data.Aeson (eitherDecodeFileStrict)
import Data.Aeson.IP ()
import Data.ByteString.Lazy qualified as LB
import Data.IP (IPRange)
import Data.Maybe (fromMaybe)
import Data.Text

-- |Parse our config file. May throw AesonException on failure.
loadConfig :: FilePath -> IO TSConfig
loadConfig fp = LB.readFile fp >>= throwDecode
import System.FilePath (takeExtension)
import Data.Yaml (decodeFileEither)

-- |Config file schema
data TSConfig
Expand All @@ -35,3 +34,27 @@ instance FromJSON TSConfig where
, tsExtraArgs = fromMaybe [] extraArgs
, tsAWSManagedPrefixLists = fromMaybe [] awsManagedPrefixLists
})

-- | Load configuration from a file, detecting format based on file extension.
loadConfig :: FilePath -> IO TSConfig
loadConfig path = do
case takeExtension path of
".json" -> loadConfigFromJSON path
".yaml" -> loadConfigFromYAML path
_ -> error "Unsupported file format. Please use .json or .yaml."

-- | Load configuration from a JSON file.
loadConfigFromJSON :: FilePath -> IO TSConfig
loadConfigFromJSON path = do
result <- eitherDecodeFileStrict path
case result of
Left err -> error $ "Failed to parse JSON config: " ++ err
Right config -> return config

-- | Load configuration from a YAML file.
loadConfigFromYAML :: FilePath -> IO TSConfig
loadConfigFromYAML path = do
result <- decodeFileEither path
case result of
Left err -> error $ "Failed to parse YAML config: " ++ show err
Right config -> return config
2 changes: 2 additions & 0 deletions tailscale-manager.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ common deps
, protolude ^>= 0.3.4
, raw-strings-qq ^>= 1.1
, text ^>= 2.0.2
, filepath ^>= 1.4.2
, yaml ^>= 0.11.8

executable tailscale-manager
import: warnings, deps
Expand Down
Loading