Merge branch 'joeyconfig' of git://git.kitenet.net/propellor into joeyconfig
Conflicts: src/Propellor/Property/SiteSpecific/IABak.hs
This commit is contained in:
commit
eb15f06896
|
@ -7,4 +7,7 @@ Setup
|
||||||
Setup.hi
|
Setup.hi
|
||||||
Setup.o
|
Setup.o
|
||||||
docker
|
docker
|
||||||
|
chroot
|
||||||
propellor.1
|
propellor.1
|
||||||
|
.lock
|
||||||
|
.lastchecked
|
||||||
|
|
14
Makefile
14
Makefile
|
@ -17,11 +17,15 @@ install:
|
||||||
cat dist/propellor-*.tar.gz | (cd dist/gittmp && tar zx --strip-components=1)
|
cat dist/propellor-*.tar.gz | (cd dist/gittmp && tar zx --strip-components=1)
|
||||||
# cabal sdist does not preserve symlinks, so copy over file
|
# cabal sdist does not preserve symlinks, so copy over file
|
||||||
cd dist/gittmp && for f in $$(find -type f); do rm -f $$f; cp -a ../../$$f $$f; done
|
cd dist/gittmp && for f in $$(find -type f); do rm -f $$f; cp -a ../../$$f $$f; done
|
||||||
cd dist/gittmp && git init && \
|
export GIT_AUTHOR_NAME=build \
|
||||||
git add . \
|
&& export GIT_AUTHOR_EMAIL=build@buildhost \
|
||||||
&& git commit -q -m "distributed version of propellor" \
|
&& export GIT_COMMITTER_NAME=build \
|
||||||
&& git bundle create $(DESTDIR)/usr/src/propellor/propellor.git master HEAD \
|
&& export GIT_COMMITTER_EMAIL=build@buildhost \
|
||||||
&& git show-ref master --hash > $(DESTDIR)/usr/src/propellor/head
|
&& cd dist/gittmp && git init \
|
||||||
|
&& git add . \
|
||||||
|
&& git commit -q -m "distributed version of propellor" \
|
||||||
|
&& git bundle create $(DESTDIR)/usr/src/propellor/propellor.git master HEAD \
|
||||||
|
&& git show-ref master --hash > $(DESTDIR)/usr/src/propellor/head
|
||||||
rm -rf dist/gittmp
|
rm -rf dist/gittmp
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
136
config-joey.hs
136
config-joey.hs
|
@ -25,6 +25,7 @@ import qualified Propellor.Property.Obnam as Obnam
|
||||||
import qualified Propellor.Property.Gpg as Gpg
|
import qualified Propellor.Property.Gpg as Gpg
|
||||||
import qualified Propellor.Property.Systemd as Systemd
|
import qualified Propellor.Property.Systemd as Systemd
|
||||||
import qualified Propellor.Property.Journald as Journald
|
import qualified Propellor.Property.Journald as Journald
|
||||||
|
import qualified Propellor.Property.Chroot as Chroot
|
||||||
import qualified Propellor.Property.OS as OS
|
import qualified Propellor.Property.OS as OS
|
||||||
import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost
|
import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost
|
||||||
import qualified Propellor.Property.HostingProvider.Linode as Linode
|
import qualified Propellor.Property.HostingProvider.Linode as Linode
|
||||||
|
@ -45,6 +46,7 @@ hosts = -- (o) `
|
||||||
, gnu
|
, gnu
|
||||||
, clam
|
, clam
|
||||||
, orca
|
, orca
|
||||||
|
, honeybee
|
||||||
, kite
|
, kite
|
||||||
, elephant
|
, elephant
|
||||||
, beaver
|
, beaver
|
||||||
|
@ -74,8 +76,6 @@ darkstar = host "darkstar.kitenet.net"
|
||||||
& ipv6 "2001:4830:1600:187::2" -- sixxs tunnel
|
& ipv6 "2001:4830:1600:187::2" -- sixxs tunnel
|
||||||
|
|
||||||
& Apt.buildDep ["git-annex"] `period` Daily
|
& Apt.buildDep ["git-annex"] `period` Daily
|
||||||
& Docker.configured
|
|
||||||
! Docker.docked gitAnnexAndroidDev
|
|
||||||
|
|
||||||
& JoeySites.postfixClientRelay (Context "darkstar.kitenet.net")
|
& JoeySites.postfixClientRelay (Context "darkstar.kitenet.net")
|
||||||
& JoeySites.dkimMilter
|
& JoeySites.dkimMilter
|
||||||
|
@ -83,7 +83,6 @@ darkstar = host "darkstar.kitenet.net"
|
||||||
gnu :: Host
|
gnu :: Host
|
||||||
gnu = host "gnu.kitenet.net"
|
gnu = host "gnu.kitenet.net"
|
||||||
& Apt.buildDep ["git-annex"] `period` Daily
|
& Apt.buildDep ["git-annex"] `period` Daily
|
||||||
& Docker.configured
|
|
||||||
|
|
||||||
& JoeySites.postfixClientRelay (Context "gnu.kitenet.net")
|
& JoeySites.postfixClientRelay (Context "gnu.kitenet.net")
|
||||||
& JoeySites.dkimMilter
|
& JoeySites.dkimMilter
|
||||||
|
@ -97,18 +96,18 @@ clam = standardSystem "clam.kitenet.net" Unstable "amd64"
|
||||||
& Ssh.randomHostKeys
|
& Ssh.randomHostKeys
|
||||||
& Apt.unattendedUpgrades
|
& Apt.unattendedUpgrades
|
||||||
& Network.ipv6to4
|
& Network.ipv6to4
|
||||||
|
|
||||||
& Tor.isRelay
|
& Tor.isRelay
|
||||||
& Tor.named "kite1"
|
& Tor.named "kite1"
|
||||||
& Tor.bandwidthRate (Tor.PerMonth "400 GB")
|
& Tor.bandwidthRate (Tor.PerMonth "400 GB")
|
||||||
|
|
||||||
& Docker.configured
|
& Systemd.nspawned webserver
|
||||||
& Docker.garbageCollected `period` Daily
|
|
||||||
& Docker.docked webserver
|
|
||||||
& File.dirExists "/var/www/html"
|
& File.dirExists "/var/www/html"
|
||||||
& File.notPresent "/var/www/html/index.html"
|
& File.notPresent "/var/www/index.html"
|
||||||
& "/var/www/index.html" `File.hasContent` ["hello, world"]
|
& "/var/www/html/index.html" `File.hasContent` ["hello, world"]
|
||||||
& alias "helloworld.kitenet.net"
|
& alias "helloworld.kitenet.net"
|
||||||
& Docker.docked oldusenetShellBox
|
|
||||||
|
& Systemd.nspawned oldusenetShellBox
|
||||||
|
|
||||||
& JoeySites.scrollBox
|
& JoeySites.scrollBox
|
||||||
& alias "scroll.joeyh.name"
|
& alias "scroll.joeyh.name"
|
||||||
|
@ -129,15 +128,46 @@ orca = standardSystem "orca.kitenet.net" Unstable "amd64"
|
||||||
|
|
||||||
& Apt.unattendedUpgrades
|
& Apt.unattendedUpgrades
|
||||||
& Postfix.satellite
|
& Postfix.satellite
|
||||||
|
& Apt.serviceInstalledRunning "ntp"
|
||||||
& Systemd.persistentJournal
|
& Systemd.persistentJournal
|
||||||
& Docker.configured
|
|
||||||
& Docker.docked (GitAnnexBuilder.standardAutoBuilderContainer dockerImage "amd64" 15 "2h")
|
& Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer
|
||||||
& Docker.docked (GitAnnexBuilder.standardAutoBuilderContainer dockerImage "i386" 45 "2h")
|
GitAnnexBuilder.standardAutoBuilder
|
||||||
& Docker.docked (GitAnnexBuilder.armelCompanionContainer dockerImage)
|
(System (Debian Unstable) "amd64") fifteenpast "2h")
|
||||||
& Docker.docked (GitAnnexBuilder.armelAutoBuilderContainer dockerImage (Cron.Times "1 3 * * *") "5h")
|
& Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer
|
||||||
& Docker.docked (GitAnnexBuilder.androidAutoBuilderContainer dockerImage (Cron.Times "1 1 * * *") "3h")
|
GitAnnexBuilder.standardAutoBuilder
|
||||||
& Docker.garbageCollected `period` Daily
|
(System (Debian Unstable) "i386") fifteenpast "2h")
|
||||||
& Apt.buildDep ["git-annex"] `period` Daily
|
& Systemd.nspawned (GitAnnexBuilder.androidAutoBuilderContainer
|
||||||
|
(Cron.Times "1 1 * * *") "3h")
|
||||||
|
where
|
||||||
|
fifteenpast = Cron.Times "15 * * * *"
|
||||||
|
|
||||||
|
honeybee :: Host
|
||||||
|
honeybee = standardSystem "honeybee.kitenet.net" Testing "armhf"
|
||||||
|
[ "Arm git-annex build box." ]
|
||||||
|
& ipv6 "2001:4830:1600:187::2"
|
||||||
|
|
||||||
|
-- No unattended upgrades as there is currently no console access.
|
||||||
|
-- (Also, system is not currently running a stock kernel,
|
||||||
|
-- although it should be able to.)
|
||||||
|
& Postfix.satellite
|
||||||
|
& Apt.serviceInstalledRunning "aiccu"
|
||||||
|
& Apt.serviceInstalledRunning "swapspace"
|
||||||
|
& Apt.serviceInstalledRunning "ntp"
|
||||||
|
|
||||||
|
-- Not using systemd-nspawn because it's broken (kernel issue?)
|
||||||
|
-- & Systemd.nspawned (GitAnnexBuilder.autoBuilderContainer
|
||||||
|
-- GitAnnexBuilder.armAutoBuilder
|
||||||
|
-- builderos Cron.Daily "22h")
|
||||||
|
& Chroot.provisioned
|
||||||
|
(Chroot.debootstrapped builderos mempty "/var/lib/container/armel-git-annex-builder"
|
||||||
|
& "/etc/timezone" `File.hasContent` ["America/New_York"]
|
||||||
|
& GitAnnexBuilder.armAutoBuilder
|
||||||
|
builderos (Cron.Times "1 1 * * *") "12h"
|
||||||
|
)
|
||||||
|
where
|
||||||
|
-- Using unstable to get new enough ghc for TH on arm.
|
||||||
|
builderos = System (Debian Unstable) "armel"
|
||||||
|
|
||||||
-- This is not a complete description of kite, since it's a
|
-- This is not a complete description of kite, since it's a
|
||||||
-- multiuser system with eg, user passwords that are not deployed
|
-- multiuser system with eg, user passwords that are not deployed
|
||||||
|
@ -222,9 +252,6 @@ kite = standardSystemUnhardened "kite.kitenet.net" Testing "amd64"
|
||||||
, "zsh"
|
, "zsh"
|
||||||
]
|
]
|
||||||
|
|
||||||
& Docker.configured
|
|
||||||
& Docker.garbageCollected `period` Daily
|
|
||||||
|
|
||||||
& alias "nntp.olduse.net"
|
& alias "nntp.olduse.net"
|
||||||
& JoeySites.oldUseNetServer hosts
|
& JoeySites.oldUseNetServer hosts
|
||||||
|
|
||||||
|
@ -281,13 +308,14 @@ elephant = standardSystem "elephant.kitenet.net" Unstable "amd64"
|
||||||
& myDnsSecondary
|
& myDnsSecondary
|
||||||
|
|
||||||
& Docker.configured
|
& Docker.configured
|
||||||
& Docker.docked oldusenetShellBox
|
|
||||||
& Docker.docked openidProvider
|
& Docker.docked openidProvider
|
||||||
`requires` Apt.serviceInstalledRunning "ntp"
|
`requires` Apt.serviceInstalledRunning "ntp"
|
||||||
& Docker.docked ancientKitenet
|
& Docker.docked ancientKitenet
|
||||||
& Docker.docked jerryPlay
|
& Docker.docked jerryPlay
|
||||||
& Docker.garbageCollected `period` (Weekly (Just 1))
|
& Docker.garbageCollected `period` (Weekly (Just 1))
|
||||||
|
|
||||||
|
& Systemd.nspawned oldusenetShellBox
|
||||||
|
|
||||||
& JoeySites.scrollBox
|
& JoeySites.scrollBox
|
||||||
& alias "scroll.joeyh.name"
|
& alias "scroll.joeyh.name"
|
||||||
& alias "eu.scroll.joeyh.name"
|
& alias "eu.scroll.joeyh.name"
|
||||||
|
@ -295,7 +323,7 @@ elephant = standardSystem "elephant.kitenet.net" Unstable "amd64"
|
||||||
-- For https port 443, shellinabox with ssh login to
|
-- For https port 443, shellinabox with ssh login to
|
||||||
-- kitenet.net
|
-- kitenet.net
|
||||||
& alias "shell.kitenet.net"
|
& alias "shell.kitenet.net"
|
||||||
& Docker.docked kiteShellBox
|
& Systemd.nspawned kiteShellBox
|
||||||
-- Nothing is using http port 80, so listen on
|
-- Nothing is using http port 80, so listen on
|
||||||
-- that port for ssh, for traveling on bad networks that
|
-- that port for ssh, for traveling on bad networks that
|
||||||
-- block 22.
|
-- block 22.
|
||||||
|
@ -316,6 +344,7 @@ beaver = host "beaver.kitenet.net"
|
||||||
-- Branchable is not completely deployed with propellor yet.
|
-- Branchable is not completely deployed with propellor yet.
|
||||||
pell :: Host
|
pell :: Host
|
||||||
pell = host "pell.branchable.com"
|
pell = host "pell.branchable.com"
|
||||||
|
& alias "branchable.com"
|
||||||
& ipv4 "66.228.46.55"
|
& ipv4 "66.228.46.55"
|
||||||
& ipv6 "2600:3c03::f03c:91ff:fedf:c0e5"
|
& ipv6 "2600:3c03::f03c:91ff:fedf:c0e5"
|
||||||
|
|
||||||
|
@ -371,22 +400,21 @@ iabak = host "iabak.archiveteam.org"
|
||||||
--' __|II| ,.
|
--' __|II| ,.
|
||||||
---- __|II|II|__ ( \_,/\
|
---- __|II|II|__ ( \_,/\
|
||||||
--'-------'\o/-'-.-'-.-'-.- __|II|II|II|II|___/ __/ -'-.-'-.-'-.-'-.-'-.-'-
|
--'-------'\o/-'-.-'-.-'-.- __|II|II|II|II|___/ __/ -'-.-'-.-'-.-'-.-'-.-'-
|
||||||
-------------------------- | [Docker] / --------------------------
|
-------------------------- | [Containers] / --------------------------
|
||||||
-------------------------- : / ---------------------------
|
-------------------------- : / ---------------------------
|
||||||
--------------------------- \____, o ,' ----------------------------
|
--------------------------- \____, o ,' ----------------------------
|
||||||
---------------------------- '--,___________,' -----------------------------
|
---------------------------- '--,___________,' -----------------------------
|
||||||
|
|
||||||
-- Simple web server, publishing the outside host's /var/www
|
-- Simple web server, publishing the outside host's /var/www
|
||||||
webserver :: Docker.Container
|
webserver :: Systemd.Container
|
||||||
webserver = standardStableContainer "webserver"
|
webserver = standardStableContainer "webserver"
|
||||||
& Docker.publish "80:80"
|
& Systemd.bind "/var/www"
|
||||||
& Docker.volume "/var/www:/var/www"
|
|
||||||
& Apt.serviceInstalledRunning "apache2"
|
& Apt.serviceInstalledRunning "apache2"
|
||||||
|
|
||||||
-- My own openid provider. Uses php, so containerized for security
|
-- My own openid provider. Uses php, so containerized for security
|
||||||
-- and administrative sanity.
|
-- and administrative sanity.
|
||||||
openidProvider :: Docker.Container
|
openidProvider :: Docker.Container
|
||||||
openidProvider = standardStableContainer "openid-provider"
|
openidProvider = standardStableDockerContainer "openid-provider"
|
||||||
& alias "openid.kitenet.net"
|
& alias "openid.kitenet.net"
|
||||||
& Docker.publish "8081:80"
|
& Docker.publish "8081:80"
|
||||||
& OpenId.providerFor [User "joey", User "liw"]
|
& OpenId.providerFor [User "joey", User "liw"]
|
||||||
|
@ -394,39 +422,30 @@ openidProvider = standardStableContainer "openid-provider"
|
||||||
|
|
||||||
-- Exhibit: kite's 90's website.
|
-- Exhibit: kite's 90's website.
|
||||||
ancientKitenet :: Docker.Container
|
ancientKitenet :: Docker.Container
|
||||||
ancientKitenet = standardStableContainer "ancient-kitenet"
|
ancientKitenet = standardStableDockerContainer "ancient-kitenet"
|
||||||
& alias "ancient.kitenet.net"
|
& alias "ancient.kitenet.net"
|
||||||
& Docker.publish "1994:80"
|
& Docker.publish "1994:80"
|
||||||
& Apt.serviceInstalledRunning "apache2"
|
& Apt.serviceInstalledRunning "apache2"
|
||||||
& Git.cloned (User "root") "git://kitenet-net.branchable.com/" "/var/www"
|
& Git.cloned (User "root") "git://kitenet-net.branchable.com/" "/var/www/html"
|
||||||
(Just "remotes/origin/old-kitenet.net")
|
(Just "remotes/origin/old-kitenet.net")
|
||||||
|
|
||||||
oldusenetShellBox :: Docker.Container
|
oldusenetShellBox :: Systemd.Container
|
||||||
oldusenetShellBox = standardStableContainer "oldusenet-shellbox"
|
oldusenetShellBox = standardStableContainer "oldusenet-shellbox"
|
||||||
& alias "shell.olduse.net"
|
& alias "shell.olduse.net"
|
||||||
& Docker.publish "4200:4200"
|
|
||||||
& JoeySites.oldUseNetShellBox
|
& JoeySites.oldUseNetShellBox
|
||||||
|
|
||||||
-- for development of git-annex for android, using my git-annex work tree
|
|
||||||
gitAnnexAndroidDev :: Docker.Container
|
|
||||||
gitAnnexAndroidDev = GitAnnexBuilder.androidContainer dockerImage "android-git-annex" doNothing gitannexdir
|
|
||||||
& Docker.volume ("/home/joey/src/git-annex:" ++ gitannexdir)
|
|
||||||
where
|
|
||||||
gitannexdir = GitAnnexBuilder.homedir </> "git-annex"
|
|
||||||
|
|
||||||
jerryPlay :: Docker.Container
|
jerryPlay :: Docker.Container
|
||||||
jerryPlay = standardContainer "jerryplay" Unstable "amd64"
|
jerryPlay = standardDockerContainer "jerryplay" Unstable "amd64"
|
||||||
& alias "jerryplay.kitenet.net"
|
& alias "jerryplay.kitenet.net"
|
||||||
& Docker.publish "2202:22"
|
& Docker.publish "2202:22"
|
||||||
& Docker.publish "8001:80"
|
& Docker.publish "8001:80"
|
||||||
& Apt.installed ["ssh"]
|
& Apt.installed ["ssh"]
|
||||||
& User.hasSomePassword (User "root")
|
& User.hasSomePassword (User "root")
|
||||||
& Ssh.permitRootLogin True
|
& Ssh.permitRootLogin (Ssh.RootLogin True)
|
||||||
|
|
||||||
kiteShellBox :: Docker.Container
|
kiteShellBox :: Systemd.Container
|
||||||
kiteShellBox = standardStableContainer "kiteshellbox"
|
kiteShellBox = standardStableContainer "kiteshellbox"
|
||||||
& JoeySites.kiteShellBox
|
& JoeySites.kiteShellBox
|
||||||
& Docker.publish "443:443"
|
|
||||||
|
|
||||||
type Motd = [String]
|
type Motd = [String]
|
||||||
|
|
||||||
|
@ -457,12 +476,25 @@ standardSystemUnhardened hn suite arch motd = host hn
|
||||||
& Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"]
|
& Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"]
|
||||||
`onChange` Apt.autoRemove
|
`onChange` Apt.autoRemove
|
||||||
|
|
||||||
standardStableContainer :: Docker.ContainerName -> Docker.Container
|
|
||||||
standardStableContainer name = standardContainer name (Stable "wheezy") "amd64"
|
|
||||||
|
|
||||||
-- This is my standard container setup, Featuring automatic upgrades.
|
-- This is my standard container setup, Featuring automatic upgrades.
|
||||||
standardContainer :: Docker.ContainerName -> DebianSuite -> Architecture -> Docker.Container
|
standardContainer :: Systemd.MachineName -> DebianSuite -> Architecture -> Systemd.Container
|
||||||
standardContainer name suite arch = Docker.container name (dockerImage system)
|
standardContainer name suite arch = Systemd.container name chroot
|
||||||
|
& os system
|
||||||
|
& Apt.stdSourcesList `onChange` Apt.upgrade
|
||||||
|
& Apt.unattendedUpgrades
|
||||||
|
& Apt.cacheCleaned
|
||||||
|
where
|
||||||
|
system = System (Debian suite) arch
|
||||||
|
chroot = Chroot.debootstrapped system mempty
|
||||||
|
|
||||||
|
standardStableContainer :: Systemd.MachineName -> Systemd.Container
|
||||||
|
standardStableContainer name = standardContainer name (Stable "jessie") "amd64"
|
||||||
|
|
||||||
|
standardStableDockerContainer :: Docker.ContainerName -> Docker.Container
|
||||||
|
standardStableDockerContainer name = standardDockerContainer name (Stable "jessie") "amd64"
|
||||||
|
|
||||||
|
standardDockerContainer :: Docker.ContainerName -> DebianSuite -> Architecture -> Docker.Container
|
||||||
|
standardDockerContainer name suite arch = Docker.container name (dockerImage system)
|
||||||
& os system
|
& os system
|
||||||
& Apt.stdSourcesList `onChange` Apt.upgrade
|
& Apt.stdSourcesList `onChange` Apt.upgrade
|
||||||
& Apt.unattendedUpgrades
|
& Apt.unattendedUpgrades
|
||||||
|
@ -473,10 +505,10 @@ standardContainer name suite arch = Docker.container name (dockerImage system)
|
||||||
|
|
||||||
-- Docker images I prefer to use.
|
-- Docker images I prefer to use.
|
||||||
dockerImage :: System -> Docker.Image
|
dockerImage :: System -> Docker.Image
|
||||||
dockerImage (System (Debian Unstable) arch) = "joeyh/debian-unstable-" ++ arch
|
dockerImage (System (Debian Unstable) arch) = Docker.latestImage ("joeyh/debian-unstable-" ++ arch)
|
||||||
dockerImage (System (Debian Testing) arch) = "joeyh/debian-unstable-" ++ arch
|
dockerImage (System (Debian Testing) arch) = Docker.latestImage ("joeyh/debian-unstable-" ++ arch)
|
||||||
dockerImage (System (Debian (Stable _)) arch) = "joeyh/debian-stable-" ++ arch
|
dockerImage (System (Debian (Stable _)) arch) = Docker.latestImage ("joeyh/debian-stable-" ++ arch)
|
||||||
dockerImage _ = "debian-stable-official" -- does not currently exist!
|
dockerImage _ = Docker.latestImage "debian-stable-official" -- does not currently exist!
|
||||||
|
|
||||||
myDnsSecondary :: Property HasInfo
|
myDnsSecondary :: Property HasInfo
|
||||||
myDnsSecondary = propertyList "dns secondary for all my domains" $ props
|
myDnsSecondary = propertyList "dns secondary for all my domains" $ props
|
||||||
|
|
|
@ -41,7 +41,7 @@ hosts =
|
||||||
|
|
||||||
-- A generic webserver in a Docker container.
|
-- A generic webserver in a Docker container.
|
||||||
webserverContainer :: Docker.Container
|
webserverContainer :: Docker.Container
|
||||||
webserverContainer = Docker.container "webserver" "debian"
|
webserverContainer = Docker.container "webserver" (Docker.latestImage "debian")
|
||||||
& os (System (Debian (Stable "jessie")) "amd64")
|
& os (System (Debian (Stable "jessie")) "amd64")
|
||||||
& Apt.stdSourcesList
|
& Apt.stdSourcesList
|
||||||
& Docker.publish "80:80"
|
& Docker.publish "80:80"
|
||||||
|
|
|
@ -1,11 +1,62 @@
|
||||||
propellor (2.5.0) UNRELEASED; urgency=medium
|
propellor (2.7.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Ssh.permitRootLogin type changed to allow configuring WithoutPassword
|
||||||
|
and ForcedCommandsOnly (API change)
|
||||||
|
* setSshdConfig type changed, and setSshdConfigBool added with old type.
|
||||||
|
* Fix a bug in shim generation code for docker and chroots, that
|
||||||
|
sometimes prevented deployment of docker containers.
|
||||||
|
* Added onChangeFlagOnFail which is often a safer alternative to
|
||||||
|
onChange.
|
||||||
|
Thanks, Antoine Eiche.
|
||||||
|
* Work around broken git pull option parser in git 2.5.0,
|
||||||
|
which broke use of --upload-pack to send a git push when running
|
||||||
|
propellor --spin.
|
||||||
|
|
||||||
|
-- Joey Hess <id@joeyh.name> Thu, 30 Jul 2015 12:05:46 -0400
|
||||||
|
|
||||||
|
propellor (2.6.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Replace String type synonym Docker.Image by a data type
|
||||||
|
which allows to specify an image name and an optional tag. (API change)
|
||||||
|
Thanks, Antoine Eiche.
|
||||||
|
* Added --unset to delete a privdata field.
|
||||||
|
* Version dependency on exceptions.
|
||||||
|
* Systemd: Add masked property.
|
||||||
|
Thanks, Sean Whitton
|
||||||
|
* Fix make install target to work even when git is not configured.
|
||||||
|
|
||||||
|
-- Joey Hess <id@joeyh.name> Fri, 10 Jul 2015 22:36:29 -0400
|
||||||
|
|
||||||
|
propellor (2.5.0) unstable; urgency=medium
|
||||||
|
|
||||||
* cmdProperty' renamed to cmdPropertyEnv to make way for a new,
|
* cmdProperty' renamed to cmdPropertyEnv to make way for a new,
|
||||||
more generic cmdProperty' (API change)
|
more generic cmdProperty' (API change)
|
||||||
* Add docker image related properties.
|
* Add docker image related properties.
|
||||||
Thanks, Antoine Eiche.
|
Thanks, Antoine Eiche.
|
||||||
|
* Export CommandParam, boolSystem, safeSystem, shellEscape, and
|
||||||
|
* createProcess from Propellor.Property.Cmd, so they are available
|
||||||
|
for use in constricting your own Properties when using propellor
|
||||||
|
as a library.
|
||||||
|
* Improve enter-machine scripts for systemd-nspawn containers to unset most
|
||||||
|
environment variables.
|
||||||
|
* Fix Postfix.satellite bug; the default relayhost was set to the
|
||||||
|
domain, not to smtp.domain as documented.
|
||||||
|
* Mount /proc inside a chroot before provisioning it, to work around #787227
|
||||||
|
* --spin now works when given a short hostname that only resolves to an
|
||||||
|
ipv6 address.
|
||||||
|
* Added publish property for systemd-spawn containers, for port publishing.
|
||||||
|
(Needs systemd version 220.)
|
||||||
|
* Added bind and bindRo properties for systemd-spawn containers.
|
||||||
|
* Firewall: Port was changed to a newtype, and the Port and PortRange
|
||||||
|
constructors of Rules were changed to DPort and DportRange, respectively.
|
||||||
|
(API change)
|
||||||
|
* Docker: volume and publish accept Bound FilePath and Bound Port,
|
||||||
|
respectively. They also continue to accept Strings, for backwards
|
||||||
|
compatibility.
|
||||||
|
* Docker: Added environment property.
|
||||||
|
Thanks Antoine Eiche.
|
||||||
|
|
||||||
-- Joey Hess <id@joeyh.name> Thu, 07 May 2015 12:08:34 -0400
|
-- Joey Hess <id@joeyh.name> Tue, 09 Jun 2015 17:08:43 -0400
|
||||||
|
|
||||||
propellor (2.4.0) unstable; urgency=medium
|
propellor (2.4.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ Build-Depends:
|
||||||
libghc-quickcheck2-dev,
|
libghc-quickcheck2-dev,
|
||||||
libghc-mtl-dev,
|
libghc-mtl-dev,
|
||||||
libghc-transformers-dev,
|
libghc-transformers-dev,
|
||||||
libghc-exceptions-dev,
|
libghc-exceptions-dev (>= 0.6),
|
||||||
Maintainer: Gergely Nagy <algernon@madhouse-project.org>
|
Maintainer: Gergely Nagy <algernon@madhouse-project.org>
|
||||||
Standards-Version: 3.9.6
|
Standards-Version: 3.9.6
|
||||||
Vcs-Git: git://git.joeyh.name/propellor
|
Vcs-Git: git://git.joeyh.name/propellor
|
||||||
|
@ -38,7 +38,7 @@ Depends: ${misc:Depends}, ${shlibs:Depends},
|
||||||
libghc-quickcheck2-dev,
|
libghc-quickcheck2-dev,
|
||||||
libghc-mtl-dev,
|
libghc-mtl-dev,
|
||||||
libghc-transformers-dev,
|
libghc-transformers-dev,
|
||||||
libghc-exceptions-dev,
|
libghc-exceptions-dev (>= 0.6),
|
||||||
git,
|
git,
|
||||||
Description: property-based host configuration management in haskell
|
Description: property-based host configuration management in haskell
|
||||||
Propellor enures that the system it's run in satisfies a list of
|
Propellor enures that the system it's run in satisfies a list of
|
||||||
|
|
|
@ -71,6 +71,10 @@ and configured in haskell.
|
||||||
|
|
||||||
Sets a field of privdata. The content is read in from stdin.
|
Sets a field of privdata. The content is read in from stdin.
|
||||||
|
|
||||||
|
* propellor --unset field context
|
||||||
|
|
||||||
|
Removes a value from the privdata store.
|
||||||
|
|
||||||
* propellor --dump field context
|
* propellor --dump field context
|
||||||
|
|
||||||
Outputs the privdata value to stdout.
|
Outputs the privdata value to stdout.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
Name: propellor
|
Name: propellor
|
||||||
Version: 2.4.0
|
Version: 2.7.0
|
||||||
Cabal-Version: >= 1.6
|
Cabal-Version: >= 1.8
|
||||||
License: BSD3
|
License: BSD3
|
||||||
Maintainer: Joey Hess <id@joeyh.name>
|
Maintainer: Joey Hess <id@joeyh.name>
|
||||||
Author: Joey Hess
|
Author: Joey Hess
|
||||||
|
@ -38,7 +38,7 @@ Executable propellor
|
||||||
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
||||||
IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal,
|
IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal,
|
||||||
containers, network, async, time, QuickCheck, mtl, transformers,
|
containers, network, async, time, QuickCheck, mtl, transformers,
|
||||||
exceptions
|
exceptions (>= 0.6)
|
||||||
|
|
||||||
if (! os(windows))
|
if (! os(windows))
|
||||||
Build-Depends: unix
|
Build-Depends: unix
|
||||||
|
@ -121,6 +121,7 @@ Library
|
||||||
Propellor.Exception
|
Propellor.Exception
|
||||||
Propellor.Types
|
Propellor.Types
|
||||||
Propellor.Types.Chroot
|
Propellor.Types.Chroot
|
||||||
|
Propellor.Types.Container
|
||||||
Propellor.Types.Docker
|
Propellor.Types.Docker
|
||||||
Propellor.Types.Dns
|
Propellor.Types.Dns
|
||||||
Propellor.Types.Empty
|
Propellor.Types.Empty
|
||||||
|
|
|
@ -6,7 +6,6 @@ module Propellor.Bootstrap (
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
import System.Posix.Files
|
import System.Posix.Files
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
|
@ -7,7 +7,7 @@ import System.Environment (getArgs)
|
||||||
import Data.List
|
import Data.List
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import System.PosixCompat
|
import System.PosixCompat
|
||||||
import qualified Network.BSD
|
import Network.Socket
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Propellor.Gpg
|
import Propellor.Gpg
|
||||||
|
@ -18,7 +18,6 @@ import Propellor.Types.CmdLine
|
||||||
import qualified Propellor.Property.Docker as Docker
|
import qualified Propellor.Property.Docker as Docker
|
||||||
import qualified Propellor.Property.Chroot as Chroot
|
import qualified Propellor.Property.Chroot as Chroot
|
||||||
import qualified Propellor.Shim as Shim
|
import qualified Propellor.Shim as Shim
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
usage :: Handle -> IO ()
|
usage :: Handle -> IO ()
|
||||||
usage h = hPutStrLn h $ unlines
|
usage h = hPutStrLn h $ unlines
|
||||||
|
@ -52,6 +51,7 @@ processCmdLine = go =<< getArgs
|
||||||
_ -> Spin <$> mapM hostname ps <*> pure Nothing
|
_ -> Spin <$> mapM hostname ps <*> pure Nothing
|
||||||
go ("--add-key":k:[]) = return $ AddKey k
|
go ("--add-key":k:[]) = return $ AddKey k
|
||||||
go ("--set":f:c:[]) = withprivfield f c Set
|
go ("--set":f:c:[]) = withprivfield f c Set
|
||||||
|
go ("--unset":f:c:[]) = withprivfield f c Unset
|
||||||
go ("--dump":f:c:[]) = withprivfield f c Dump
|
go ("--dump":f:c:[]) = withprivfield f c Dump
|
||||||
go ("--edit":f:c:[]) = withprivfield f c Edit
|
go ("--edit":f:c:[]) = withprivfield f c Edit
|
||||||
go ("--list-fields":[]) = return ListFields
|
go ("--list-fields":[]) = return ListFields
|
||||||
|
@ -95,6 +95,7 @@ defaultMain hostlist = do
|
||||||
go _ (Continue cmdline) = go False cmdline
|
go _ (Continue cmdline) = go False cmdline
|
||||||
go _ Check = return ()
|
go _ Check = return ()
|
||||||
go _ (Set field context) = setPrivData field context
|
go _ (Set field context) = setPrivData field context
|
||||||
|
go _ (Unset field context) = unsetPrivData field context
|
||||||
go _ (Dump field context) = dumpPrivData field context
|
go _ (Dump field context) = dumpPrivData field context
|
||||||
go _ (Edit field context) = editPrivData field context
|
go _ (Edit field context) = editPrivData field context
|
||||||
go _ ListFields = listPrivDataFields hostlist
|
go _ ListFields = listPrivDataFields hostlist
|
||||||
|
@ -166,9 +167,15 @@ updateFirst' cmdline next = ifM fetchOrigin
|
||||||
, next
|
, next
|
||||||
)
|
)
|
||||||
|
|
||||||
|
-- Gets the fully qualified domain name, given a string that might be
|
||||||
|
-- a short name to look up in the DNS.
|
||||||
hostname :: String -> IO HostName
|
hostname :: String -> IO HostName
|
||||||
hostname s
|
hostname s = go =<< catchDefaultIO [] dnslookup
|
||||||
| "." `isInfixOf` s = pure s
|
where
|
||||||
| otherwise = do
|
dnslookup = getAddrInfo (Just canonname) (Just s) Nothing
|
||||||
h <- Network.BSD.getHostByName s
|
canonname = defaultHints { addrFlags = [AI_CANONNAME] }
|
||||||
return (Network.BSD.hostName h)
|
go (AddrInfo { addrCanonName = Just v } : _) = pure v
|
||||||
|
go _
|
||||||
|
| "." `isInfixOf` s = pure s -- assume it's a fqdn
|
||||||
|
| otherwise =
|
||||||
|
error $ "cannot find host " ++ s ++ " in the DNS"
|
||||||
|
|
|
@ -3,7 +3,6 @@ module Propellor.Git where
|
||||||
import Propellor
|
import Propellor
|
||||||
import Propellor.PrivData.Paths
|
import Propellor.PrivData.Paths
|
||||||
import Propellor.Gpg
|
import Propellor.Gpg
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
getCurrentBranch :: IO String
|
getCurrentBranch :: IO String
|
||||||
|
|
|
@ -6,6 +6,7 @@ module Propellor.PrivData (
|
||||||
withSomePrivData,
|
withSomePrivData,
|
||||||
addPrivData,
|
addPrivData,
|
||||||
setPrivData,
|
setPrivData,
|
||||||
|
unsetPrivData,
|
||||||
dumpPrivData,
|
dumpPrivData,
|
||||||
editPrivData,
|
editPrivData,
|
||||||
filterPrivData,
|
filterPrivData,
|
||||||
|
@ -143,6 +144,11 @@ setPrivData field context = do
|
||||||
putStrLn "Enter private data on stdin; ctrl-D when done:"
|
putStrLn "Enter private data on stdin; ctrl-D when done:"
|
||||||
setPrivDataTo field context =<< hGetContentsStrict stdin
|
setPrivDataTo field context =<< hGetContentsStrict stdin
|
||||||
|
|
||||||
|
unsetPrivData :: PrivDataField -> Context -> IO ()
|
||||||
|
unsetPrivData field context = do
|
||||||
|
modifyPrivData $ M.delete (field, context)
|
||||||
|
putStrLn "Private data unset."
|
||||||
|
|
||||||
dumpPrivData :: PrivDataField -> Context -> IO ()
|
dumpPrivData :: PrivDataField -> Context -> IO ()
|
||||||
dumpPrivData field context =
|
dumpPrivData field context =
|
||||||
maybe (error "Requested privdata is not set.") putStrLn
|
maybe (error "Requested privdata is not set.") putStrLn
|
||||||
|
@ -192,17 +198,22 @@ listPrivDataFields hosts = do
|
||||||
|
|
||||||
setPrivDataTo :: PrivDataField -> Context -> PrivData -> IO ()
|
setPrivDataTo :: PrivDataField -> Context -> PrivData -> IO ()
|
||||||
setPrivDataTo field context value = do
|
setPrivDataTo field context value = do
|
||||||
makePrivDataDir
|
modifyPrivData set
|
||||||
m <- decryptPrivData
|
|
||||||
let m' = M.insert (field, context) (chomp value) m
|
|
||||||
gpgEncrypt privDataFile (show m')
|
|
||||||
putStrLn "Private data set."
|
putStrLn "Private data set."
|
||||||
void $ boolSystem "git" [Param "add", File privDataFile]
|
|
||||||
where
|
where
|
||||||
|
set = M.insert (field, context) (chomp value)
|
||||||
chomp s
|
chomp s
|
||||||
| end s == "\n" = chomp (beginning s)
|
| end s == "\n" = chomp (beginning s)
|
||||||
| otherwise = s
|
| otherwise = s
|
||||||
|
|
||||||
|
modifyPrivData :: (PrivMap -> PrivMap) -> IO ()
|
||||||
|
modifyPrivData f = do
|
||||||
|
makePrivDataDir
|
||||||
|
m <- decryptPrivData
|
||||||
|
let m' = f m
|
||||||
|
gpgEncrypt privDataFile (show m')
|
||||||
|
void $ boolSystem "git" [Param "add", File privDataFile]
|
||||||
|
|
||||||
decryptPrivData :: IO PrivMap
|
decryptPrivData :: IO PrivMap
|
||||||
decryptPrivData = fromMaybe M.empty . readish <$> gpgDecrypt privDataFile
|
decryptPrivData = fromMaybe M.empty . readish <$> gpgDecrypt privDataFile
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,41 @@ onChange = combineWith $ \p hook -> do
|
||||||
return $ r <> r'
|
return $ r <> r'
|
||||||
_ -> return r
|
_ -> return r
|
||||||
|
|
||||||
|
-- | Same as `onChange` except that if property y fails, a flag file
|
||||||
|
-- is generated. On next run, if the flag file is present, property y
|
||||||
|
-- is executed even if property x doesn't change.
|
||||||
|
--
|
||||||
|
-- With `onChange`, if y fails, the property x `onChange` y returns
|
||||||
|
-- `FailedChange`. But if this property is applied again, it returns
|
||||||
|
-- `NoChange`. This behavior can cause trouble...
|
||||||
|
onChangeFlagOnFail
|
||||||
|
:: (Combines (Property x) (Property y))
|
||||||
|
=> FilePath
|
||||||
|
-> Property x
|
||||||
|
-> Property y
|
||||||
|
-> CombinedType (Property x) (Property y)
|
||||||
|
onChangeFlagOnFail flagfile p1 p2 =
|
||||||
|
combineWith go p1 p2
|
||||||
|
where
|
||||||
|
go s1 s2 = do
|
||||||
|
r1 <- s1
|
||||||
|
case r1 of
|
||||||
|
MadeChange -> flagFailed s2
|
||||||
|
_ -> ifM (liftIO $ doesFileExist flagfile)
|
||||||
|
(flagFailed s2
|
||||||
|
, return r1
|
||||||
|
)
|
||||||
|
flagFailed s = do
|
||||||
|
r <- s
|
||||||
|
liftIO $ case r of
|
||||||
|
FailedChange -> createFlagFile
|
||||||
|
_ -> removeFlagFile
|
||||||
|
return r
|
||||||
|
createFlagFile = unlessM (doesFileExist flagfile) $ do
|
||||||
|
createDirectoryIfMissing True (takeDirectory flagfile)
|
||||||
|
writeFile flagfile ""
|
||||||
|
removeFlagFile = whenM (doesFileExist flagfile) $ removeFile flagfile
|
||||||
|
|
||||||
-- | Alias for @flip describe@
|
-- | Alias for @flip describe@
|
||||||
(==>) :: IsProp (Property i) => Desc -> Property i -> Property i
|
(==>) :: IsProp (Property i) => Desc -> Property i -> Property i
|
||||||
(==>) = flip describe
|
(==>) = flip describe
|
||||||
|
|
|
@ -4,7 +4,6 @@ import Propellor
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Service as Service
|
import qualified Propellor.Property.Service as Service
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
type ConfigFile = [String]
|
type ConfigFile = [String]
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import Propellor.Property.Chroot.Util
|
||||||
import qualified Propellor.Property.Debootstrap as Debootstrap
|
import qualified Propellor.Property.Debootstrap as Debootstrap
|
||||||
import qualified Propellor.Property.Systemd.Core as Systemd
|
import qualified Propellor.Property.Systemd.Core as Systemd
|
||||||
import qualified Propellor.Shim as Shim
|
import qualified Propellor.Shim as Shim
|
||||||
import Utility.SafeCommand
|
import Propellor.Property.Mount
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import Data.List.Utils
|
import Data.List.Utils
|
||||||
|
@ -56,8 +56,9 @@ debootstrapped system conf location = case system of
|
||||||
-- | Ensures that the chroot exists and is provisioned according to its
|
-- | Ensures that the chroot exists and is provisioned according to its
|
||||||
-- properties.
|
-- properties.
|
||||||
--
|
--
|
||||||
-- Reverting this property removes the chroot. Note that it does not ensure
|
-- Reverting this property removes the chroot. Anything mounted inside it
|
||||||
-- that any processes that might be running inside the chroot are stopped.
|
-- is first unmounted. Note that it does not ensure that any processes
|
||||||
|
-- that might be running inside the chroot are stopped.
|
||||||
provisioned :: Chroot -> RevertableProperty
|
provisioned :: Chroot -> RevertableProperty
|
||||||
provisioned c = provisioned' (propigateChrootInfo c) c False
|
provisioned c = provisioned' (propigateChrootInfo c) c False
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ provisioned' propigator c@(Chroot loc system builderconf _) systemdonly =
|
||||||
where
|
where
|
||||||
go desc a = propertyList (chrootDesc c desc) [a]
|
go desc a = propertyList (chrootDesc c desc) [a]
|
||||||
|
|
||||||
setup = propellChroot c (inChrootProcess c) systemdonly
|
setup = propellChroot c (inChrootProcess (not systemdonly) c) systemdonly
|
||||||
`requires` toProp built
|
`requires` toProp built
|
||||||
|
|
||||||
built = case (system, builderconf) of
|
built = case (system, builderconf) of
|
||||||
|
@ -94,7 +95,7 @@ chrootInfo (Chroot loc _ _ h) =
|
||||||
mempty { _chrootinfo = mempty { _chroots = M.singleton loc h } }
|
mempty { _chrootinfo = mempty { _chroots = M.singleton loc h } }
|
||||||
|
|
||||||
-- | Propellor is run inside the chroot to provision it.
|
-- | Propellor is run inside the chroot to provision it.
|
||||||
propellChroot :: Chroot -> ([String] -> CreateProcess) -> Bool -> Property NoInfo
|
propellChroot :: Chroot -> ([String] -> IO (CreateProcess, IO ())) -> Bool -> Property NoInfo
|
||||||
propellChroot c@(Chroot loc _ _ _) mkproc systemdonly = property (chrootDesc c "provisioned") $ do
|
propellChroot c@(Chroot loc _ _ _) mkproc systemdonly = property (chrootDesc c "provisioned") $ do
|
||||||
let d = localdir </> shimdir c
|
let d = localdir </> shimdir c
|
||||||
let me = localdir </> "propellor"
|
let me = localdir </> "propellor"
|
||||||
|
@ -117,19 +118,21 @@ propellChroot c@(Chroot loc _ _ _) mkproc systemdonly = property (chrootDesc c "
|
||||||
, File localdir, File mntpnt
|
, File localdir, File mntpnt
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
chainprovision shim = do
|
chainprovision shim = do
|
||||||
parenthost <- asks hostName
|
parenthost <- asks hostName
|
||||||
cmd <- liftIO $ toChain parenthost c systemdonly
|
cmd <- liftIO $ toChain parenthost c systemdonly
|
||||||
pe <- liftIO standardPathEnv
|
pe <- liftIO standardPathEnv
|
||||||
let p = mkproc
|
(p, cleanup) <- liftIO $ mkproc
|
||||||
[ shim
|
[ shim
|
||||||
, "--continue"
|
, "--continue"
|
||||||
, show cmd
|
, show cmd
|
||||||
]
|
]
|
||||||
let p' = p { env = Just pe }
|
let p' = p { env = Just pe }
|
||||||
liftIO $ withHandle StdoutHandle createProcessSuccess p'
|
r <- liftIO $ withHandle StdoutHandle createProcessSuccess p'
|
||||||
processChainOutput
|
processChainOutput
|
||||||
|
liftIO cleanup
|
||||||
|
return r
|
||||||
|
|
||||||
toChain :: HostName -> Chroot -> Bool -> IO CmdLine
|
toChain :: HostName -> Chroot -> Bool -> IO CmdLine
|
||||||
toChain parenthost (Chroot loc _ _ _) systemdonly = do
|
toChain parenthost (Chroot loc _ _ _) systemdonly = do
|
||||||
|
@ -156,8 +159,22 @@ chain hostlist (ChrootChain hn loc systemdonly onconsole) =
|
||||||
putStrLn $ "\n" ++ show r
|
putStrLn $ "\n" ++ show r
|
||||||
chain _ _ = errorMessage "bad chain command"
|
chain _ _ = errorMessage "bad chain command"
|
||||||
|
|
||||||
inChrootProcess :: Chroot -> [String] -> CreateProcess
|
inChrootProcess :: Bool -> Chroot -> [String] -> IO (CreateProcess, IO ())
|
||||||
inChrootProcess (Chroot loc _ _ _) cmd = proc "chroot" (loc:cmd)
|
inChrootProcess keepprocmounted (Chroot loc _ _ _) cmd = do
|
||||||
|
mountproc
|
||||||
|
return (proc "chroot" (loc:cmd), cleanup)
|
||||||
|
where
|
||||||
|
-- /proc needs to be mounted in the chroot for the linker to use
|
||||||
|
-- /proc/self/exe which is necessary for some commands to work
|
||||||
|
mountproc = unlessM (elem procloc <$> mountPointsBelow loc) $
|
||||||
|
void $ mount "proc" "proc" procloc
|
||||||
|
|
||||||
|
procloc = loc </> "proc"
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
| keepprocmounted = noop
|
||||||
|
| otherwise = whenM (elem procloc <$> mountPointsBelow loc) $
|
||||||
|
umountLazy procloc
|
||||||
|
|
||||||
provisioningLock :: FilePath -> FilePath
|
provisioningLock :: FilePath -> FilePath
|
||||||
provisioningLock containerloc = "chroot" </> mungeloc containerloc ++ ".lock"
|
provisioningLock containerloc = "chroot" </> mungeloc containerloc ++ ".lock"
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
|
|
||||||
module Propellor.Property.Cmd (
|
module Propellor.Property.Cmd (
|
||||||
|
-- * Properties for running commands and scripts
|
||||||
cmdProperty,
|
cmdProperty,
|
||||||
cmdProperty',
|
cmdProperty',
|
||||||
cmdPropertyEnv,
|
cmdPropertyEnv,
|
||||||
|
Script,
|
||||||
scriptProperty,
|
scriptProperty,
|
||||||
userScriptProperty,
|
userScriptProperty,
|
||||||
|
-- * Lower-level interface for running commands
|
||||||
|
CommandParam(..),
|
||||||
|
boolSystem,
|
||||||
|
boolSystemEnv,
|
||||||
|
safeSystem,
|
||||||
|
safeSystemEnv,
|
||||||
|
shellEscape,
|
||||||
|
createProcess,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Data.List
|
import Data.List
|
||||||
import "mtl" Control.Monad.Reader
|
import "mtl" Control.Monad.Reader
|
||||||
import System.Process (CreateProcess)
|
|
||||||
|
|
||||||
import Propellor.Types
|
import Propellor.Types
|
||||||
import Propellor.Property
|
import Propellor.Property
|
||||||
import Utility.SafeCommand
|
import Utility.SafeCommand
|
||||||
import Utility.Env
|
import Utility.Env
|
||||||
|
import Utility.Process (createProcess, CreateProcess)
|
||||||
|
|
||||||
-- | A property that can be satisfied by running a command.
|
-- | A property that can be satisfied by running a command.
|
||||||
--
|
--
|
||||||
|
@ -40,15 +50,18 @@ cmdPropertyEnv cmd params env = property desc $ liftIO $ do
|
||||||
where
|
where
|
||||||
desc = unwords $ cmd : params
|
desc = unwords $ cmd : params
|
||||||
|
|
||||||
-- | A property that can be satisfied by running a series of shell commands.
|
-- | A series of shell commands. (Without a leading hashbang.)
|
||||||
scriptProperty :: [String] -> Property NoInfo
|
type Script = [String]
|
||||||
|
|
||||||
|
-- | A property that can be satisfied by running a script.
|
||||||
|
scriptProperty :: Script -> Property NoInfo
|
||||||
scriptProperty script = cmdProperty "sh" ["-c", shellcmd]
|
scriptProperty script = cmdProperty "sh" ["-c", shellcmd]
|
||||||
where
|
where
|
||||||
shellcmd = intercalate " ; " ("set -e" : script)
|
shellcmd = intercalate " ; " ("set -e" : script)
|
||||||
|
|
||||||
-- | A property that can satisfied by running a series of shell commands,
|
-- | A property that can satisfied by running a script
|
||||||
-- as user (cd'd to their home directory).
|
-- as user (cd'd to their home directory).
|
||||||
userScriptProperty :: User -> [String] -> Property NoInfo
|
userScriptProperty :: User -> Script -> Property NoInfo
|
||||||
userScriptProperty (User user) script = cmdProperty "su" ["--shell", "/bin/sh", "-c", shellcmd, user]
|
userScriptProperty (User user) script = cmdProperty "su" ["--shell", "/bin/sh", "-c", shellcmd, user]
|
||||||
where
|
where
|
||||||
shellcmd = intercalate " ; " ("set -e" : "cd" : script)
|
shellcmd = intercalate " ; " ("set -e" : "cd" : script)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import Propellor
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import Propellor.Bootstrap
|
import Propellor.Bootstrap
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
|
|
@ -15,7 +15,6 @@ import qualified Propellor.Property.Apt as Apt
|
||||||
import Propellor.Property.Chroot.Util
|
import Propellor.Property.Chroot.Util
|
||||||
import Propellor.Property.Mount
|
import Propellor.Property.Mount
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
@ -107,9 +106,7 @@ unpopulated d = null <$> catchDefaultIO [] (dirContents d)
|
||||||
|
|
||||||
removetarget :: FilePath -> IO ()
|
removetarget :: FilePath -> IO ()
|
||||||
removetarget target = do
|
removetarget target = do
|
||||||
submnts <- filter (\p -> simplifyPath p /= simplifyPath target)
|
submnts <- mountPointsBelow target
|
||||||
. filter (dirContains target)
|
|
||||||
<$> mountPoints
|
|
||||||
forM_ submnts umountLazy
|
forM_ submnts umountLazy
|
||||||
removeDirectoryRecursive target
|
removeDirectoryRecursive target
|
||||||
|
|
||||||
|
|
|
@ -16,22 +16,26 @@ module Propellor.Property.Docker (
|
||||||
memoryLimited,
|
memoryLimited,
|
||||||
garbageCollected,
|
garbageCollected,
|
||||||
tweaked,
|
tweaked,
|
||||||
Image,
|
Image(..),
|
||||||
|
latestImage,
|
||||||
ContainerName,
|
ContainerName,
|
||||||
Container,
|
Container,
|
||||||
HasImage(..),
|
HasImage(..),
|
||||||
-- * Container configuration
|
-- * Container configuration
|
||||||
dns,
|
dns,
|
||||||
hostname,
|
hostname,
|
||||||
|
Publishable,
|
||||||
publish,
|
publish,
|
||||||
expose,
|
expose,
|
||||||
user,
|
user,
|
||||||
|
Mountable,
|
||||||
volume,
|
volume,
|
||||||
volumes_from,
|
volumes_from,
|
||||||
workdir,
|
workdir,
|
||||||
memory,
|
memory,
|
||||||
cpuShares,
|
cpuShares,
|
||||||
link,
|
link,
|
||||||
|
environment,
|
||||||
ContainerAlias,
|
ContainerAlias,
|
||||||
restartAlways,
|
restartAlways,
|
||||||
restartOnFailure,
|
restartOnFailure,
|
||||||
|
@ -43,12 +47,12 @@ module Propellor.Property.Docker (
|
||||||
|
|
||||||
import Propellor hiding (init)
|
import Propellor hiding (init)
|
||||||
import Propellor.Types.Docker
|
import Propellor.Types.Docker
|
||||||
|
import Propellor.Types.Container
|
||||||
import Propellor.Types.CmdLine
|
import Propellor.Types.CmdLine
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Cmd as Cmd
|
import qualified Propellor.Property.Cmd as Cmd
|
||||||
import qualified Propellor.Shim as Shim
|
import qualified Propellor.Shim as Shim
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
import Utility.ThreadScheduler
|
import Utility.ThreadScheduler
|
||||||
|
|
||||||
|
@ -152,8 +156,8 @@ docked ctr@(Container _ h) =
|
||||||
imageBuilt :: HasImage c => FilePath -> c -> Property NoInfo
|
imageBuilt :: HasImage c => FilePath -> c -> Property NoInfo
|
||||||
imageBuilt directory ctr = describe built msg
|
imageBuilt directory ctr = describe built msg
|
||||||
where
|
where
|
||||||
msg = "docker image " ++ image ++ " built from " ++ directory
|
msg = "docker image " ++ (imageIdentifier image) ++ " built from " ++ directory
|
||||||
built = Cmd.cmdProperty' dockercmd ["build", "--tag", image, "./"] workDir
|
built = Cmd.cmdProperty' dockercmd ["build", "--tag", imageIdentifier image, "./"] workDir
|
||||||
workDir p = p { cwd = Just directory }
|
workDir p = p { cwd = Just directory }
|
||||||
image = getImageName ctr
|
image = getImageName ctr
|
||||||
|
|
||||||
|
@ -161,8 +165,8 @@ imageBuilt directory ctr = describe built msg
|
||||||
imagePulled :: HasImage c => c -> Property NoInfo
|
imagePulled :: HasImage c => c -> Property NoInfo
|
||||||
imagePulled ctr = describe pulled msg
|
imagePulled ctr = describe pulled msg
|
||||||
where
|
where
|
||||||
msg = "docker image " ++ image ++ " pulled"
|
msg = "docker image " ++ (imageIdentifier image) ++ " pulled"
|
||||||
pulled = Cmd.cmdProperty dockercmd ["pull", image]
|
pulled = Cmd.cmdProperty dockercmd ["pull", imageIdentifier image]
|
||||||
image = getImageName ctr
|
image = getImageName ctr
|
||||||
|
|
||||||
propigateContainerInfo :: (IsProp (Property i)) => Container -> Property i -> Property HasInfo
|
propigateContainerInfo :: (IsProp (Property i)) => Container -> Property i -> Property HasInfo
|
||||||
|
@ -240,8 +244,52 @@ data ContainerInfo = ContainerInfo Image [RunParam]
|
||||||
-- | Parameters to pass to `docker run` when creating a container.
|
-- | Parameters to pass to `docker run` when creating a container.
|
||||||
type RunParam = String
|
type RunParam = String
|
||||||
|
|
||||||
-- | A docker image, that can be used to run a container.
|
-- | ImageID is an image identifier to perform action on images. An
|
||||||
type Image = String
|
-- ImageID can be the name of an container image, a UID, etc.
|
||||||
|
--
|
||||||
|
-- It just encapsulates a String to avoid the definition of a String
|
||||||
|
-- instance of ImageIdentifier.
|
||||||
|
newtype ImageID = ImageID String
|
||||||
|
|
||||||
|
-- | Used to perform Docker action on an image.
|
||||||
|
--
|
||||||
|
-- Minimal complete definition: `imageIdentifier`
|
||||||
|
class ImageIdentifier i where
|
||||||
|
-- | For internal purposes only.
|
||||||
|
toImageID :: i -> ImageID
|
||||||
|
toImageID = ImageID . imageIdentifier
|
||||||
|
-- | A string that Docker can use as an image identifier.
|
||||||
|
imageIdentifier :: i -> String
|
||||||
|
|
||||||
|
instance ImageIdentifier ImageID where
|
||||||
|
imageIdentifier (ImageID i) = i
|
||||||
|
toImageID = id
|
||||||
|
|
||||||
|
-- | A docker image, that can be used to run a container. The user has
|
||||||
|
-- to specify a name and can provide an optional tag.
|
||||||
|
-- See <http://docs.docker.com/userguide/dockerimages/ Docker Image Documention>
|
||||||
|
-- for more information.
|
||||||
|
data Image = Image
|
||||||
|
{ repository :: String
|
||||||
|
, tag :: Maybe String
|
||||||
|
}
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | Defines a Docker image without any tag. This is considered by
|
||||||
|
-- Docker as the latest image of the provided repository.
|
||||||
|
latestImage :: String -> Image
|
||||||
|
latestImage repo = Image repo Nothing
|
||||||
|
|
||||||
|
instance ImageIdentifier Image where
|
||||||
|
-- | The format of the imageIdentifier of an `Image` is:
|
||||||
|
-- repository | repository:tag
|
||||||
|
imageIdentifier i = repository i ++ (maybe "" ((++) ":") $ tag i)
|
||||||
|
|
||||||
|
-- | The UID of an image. This UID is generated by Docker.
|
||||||
|
newtype ImageUID = ImageUID String
|
||||||
|
|
||||||
|
instance ImageIdentifier ImageUID where
|
||||||
|
imageIdentifier (ImageUID uid) = uid
|
||||||
|
|
||||||
-- | Set custom dns server for container.
|
-- | Set custom dns server for container.
|
||||||
dns :: String -> Property HasInfo
|
dns :: String -> Property HasInfo
|
||||||
|
@ -255,10 +303,19 @@ hostname = runProp "hostname"
|
||||||
name :: String -> Property HasInfo
|
name :: String -> Property HasInfo
|
||||||
name = runProp "name"
|
name = runProp "name"
|
||||||
|
|
||||||
|
class Publishable p where
|
||||||
|
toPublish :: p -> String
|
||||||
|
|
||||||
|
instance Publishable (Bound Port) where
|
||||||
|
toPublish p = show (hostSide p) ++ ":" ++ show (containerSide p)
|
||||||
|
|
||||||
|
-- | string format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
|
||||||
|
instance Publishable String where
|
||||||
|
toPublish = id
|
||||||
|
|
||||||
-- | Publish a container's port to the host
|
-- | Publish a container's port to the host
|
||||||
-- (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort)
|
publish :: Publishable p => p -> Property HasInfo
|
||||||
publish :: String -> Property HasInfo
|
publish = runProp "publish" . toPublish
|
||||||
publish = runProp "publish"
|
|
||||||
|
|
||||||
-- | Expose a container's port without publishing it.
|
-- | Expose a container's port without publishing it.
|
||||||
expose :: String -> Property HasInfo
|
expose :: String -> Property HasInfo
|
||||||
|
@ -268,11 +325,21 @@ expose = runProp "expose"
|
||||||
user :: String -> Property HasInfo
|
user :: String -> Property HasInfo
|
||||||
user = runProp "user"
|
user = runProp "user"
|
||||||
|
|
||||||
-- | Mount a volume
|
class Mountable p where
|
||||||
-- Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]
|
toMount :: p -> String
|
||||||
|
|
||||||
|
instance Mountable (Bound FilePath) where
|
||||||
|
toMount p = hostSide p ++ ":" ++ containerSide p
|
||||||
|
|
||||||
|
-- | string format: [host-dir]:[container-dir]:[rw|ro]
|
||||||
|
--
|
||||||
-- With just a directory, creates a volume in the container.
|
-- With just a directory, creates a volume in the container.
|
||||||
volume :: String -> Property HasInfo
|
instance Mountable String where
|
||||||
volume = runProp "volume"
|
toMount = id
|
||||||
|
|
||||||
|
-- | Mount a volume
|
||||||
|
volume :: Mountable v => v -> Property HasInfo
|
||||||
|
volume = runProp "volume" . toMount
|
||||||
|
|
||||||
-- | Mount a volume from the specified container into the current
|
-- | Mount a volume from the specified container into the current
|
||||||
-- container.
|
-- container.
|
||||||
|
@ -327,6 +394,11 @@ restartOnFailure (Just n) = runProp "restart" ("on-failure:" ++ show n)
|
||||||
restartNever :: Property HasInfo
|
restartNever :: Property HasInfo
|
||||||
restartNever = runProp "restart" "no"
|
restartNever = runProp "restart" "no"
|
||||||
|
|
||||||
|
-- | Set environment variable with a tuple composed by the environment
|
||||||
|
-- variable name and its value.
|
||||||
|
environment :: (String, String) -> Property HasInfo
|
||||||
|
environment (k, v) = runProp "env" $ k ++ "=" ++ v
|
||||||
|
|
||||||
-- | A container is identified by its name, and the host
|
-- | A container is identified by its name, and the host
|
||||||
-- on which it's deployed.
|
-- on which it's deployed.
|
||||||
data ContainerId = ContainerId
|
data ContainerId = ContainerId
|
||||||
|
@ -397,7 +469,9 @@ runningContainer cid@(ContainerId hn cn) image runps = containerDesc cid $ prope
|
||||||
return FailedChange
|
return FailedChange
|
||||||
|
|
||||||
restartcontainer = do
|
restartcontainer = do
|
||||||
oldimage <- liftIO $ fromMaybe image <$> commitContainer cid
|
oldimage <- liftIO $
|
||||||
|
fromMaybe (toImageID image) . fmap toImageID <$>
|
||||||
|
commitContainer cid
|
||||||
void $ liftIO $ removeContainer cid
|
void $ liftIO $ removeContainer cid
|
||||||
go oldimage
|
go oldimage
|
||||||
|
|
||||||
|
@ -426,16 +500,14 @@ runningContainer cid@(ContainerId hn cn) image runps = containerDesc cid $ prope
|
||||||
retry (n-1) a
|
retry (n-1) a
|
||||||
_ -> return v
|
_ -> return v
|
||||||
|
|
||||||
go img = do
|
go img = liftIO $ do
|
||||||
liftIO $ do
|
clearProvisionedFlag cid
|
||||||
clearProvisionedFlag cid
|
createDirectoryIfMissing True (takeDirectory $ identFile cid)
|
||||||
createDirectoryIfMissing True (takeDirectory $ identFile cid)
|
shim <- Shim.setup (localdir </> "propellor") Nothing (localdir </> shimdir cid)
|
||||||
shim <- liftIO $ Shim.setup (localdir </> "propellor") Nothing (localdir </> shimdir cid)
|
writeFile (identFile cid) (show ident)
|
||||||
liftIO $ writeFile (identFile cid) (show ident)
|
toResult <$> runContainer img
|
||||||
ensureProperty $ property "run" $ liftIO $
|
(runps ++ ["-i", "-d", "-t"])
|
||||||
toResult <$> runContainer img
|
[shim, "--continue", show (DockerInit (fromContainerId cid))]
|
||||||
(runps ++ ["-i", "-d", "-t"])
|
|
||||||
[shim, "--continue", show (DockerInit (fromContainerId cid))]
|
|
||||||
|
|
||||||
-- | Called when propellor is running inside a docker container.
|
-- | Called when propellor is running inside a docker container.
|
||||||
-- The string should be the container's ContainerId.
|
-- The string should be the container's ContainerId.
|
||||||
|
@ -536,20 +608,20 @@ removeContainer :: ContainerId -> IO Bool
|
||||||
removeContainer cid = catchBoolIO $
|
removeContainer cid = catchBoolIO $
|
||||||
snd <$> processTranscript dockercmd ["rm", fromContainerId cid ] Nothing
|
snd <$> processTranscript dockercmd ["rm", fromContainerId cid ] Nothing
|
||||||
|
|
||||||
removeImage :: Image -> IO Bool
|
removeImage :: ImageIdentifier i => i -> IO Bool
|
||||||
removeImage image = catchBoolIO $
|
removeImage image = catchBoolIO $
|
||||||
snd <$> processTranscript dockercmd ["rmi", image ] Nothing
|
snd <$> processTranscript dockercmd ["rmi", imageIdentifier image] Nothing
|
||||||
|
|
||||||
runContainer :: Image -> [RunParam] -> [String] -> IO Bool
|
runContainer :: ImageIdentifier i => i -> [RunParam] -> [String] -> IO Bool
|
||||||
runContainer image ps cmd = boolSystem dockercmd $ map Param $
|
runContainer image ps cmd = boolSystem dockercmd $ map Param $
|
||||||
"run" : (ps ++ image : cmd)
|
"run" : (ps ++ (imageIdentifier image) : cmd)
|
||||||
|
|
||||||
inContainerProcess :: ContainerId -> [String] -> [String] -> CreateProcess
|
inContainerProcess :: ContainerId -> [String] -> [String] -> CreateProcess
|
||||||
inContainerProcess cid ps cmd = proc dockercmd ("exec" : ps ++ [fromContainerId cid] ++ cmd)
|
inContainerProcess cid ps cmd = proc dockercmd ("exec" : ps ++ [fromContainerId cid] ++ cmd)
|
||||||
|
|
||||||
commitContainer :: ContainerId -> IO (Maybe Image)
|
commitContainer :: ContainerId -> IO (Maybe ImageUID)
|
||||||
commitContainer cid = catchMaybeIO $
|
commitContainer cid = catchMaybeIO $
|
||||||
takeWhile (/= '\n')
|
ImageUID . takeWhile (/= '\n')
|
||||||
<$> readProcess dockercmd ["commit", fromContainerId cid]
|
<$> readProcess dockercmd ["commit", fromContainerId cid]
|
||||||
|
|
||||||
data ContainerFilter = RunningContainers | AllContainers
|
data ContainerFilter = RunningContainers | AllContainers
|
||||||
|
@ -567,8 +639,8 @@ listContainers status =
|
||||||
| otherwise = baseps
|
| otherwise = baseps
|
||||||
baseps = ["ps", "--no-trunc"]
|
baseps = ["ps", "--no-trunc"]
|
||||||
|
|
||||||
listImages :: IO [Image]
|
listImages :: IO [ImageUID]
|
||||||
listImages = lines <$> readProcess dockercmd ["images", "--all", "--quiet"]
|
listImages = map ImageUID . lines <$> readProcess dockercmd ["images", "--all", "--quiet"]
|
||||||
|
|
||||||
runProp :: String -> RunParam -> Property HasInfo
|
runProp :: String -> RunParam -> Property HasInfo
|
||||||
runProp field val = pureInfoProperty (param) $ dockerInfo $
|
runProp field val = pureInfoProperty (param) $ dockerInfo $
|
||||||
|
|
|
@ -9,7 +9,6 @@ module Propellor.Property.Firewall (
|
||||||
Target(..),
|
Target(..),
|
||||||
Proto(..),
|
Proto(..),
|
||||||
Rules(..),
|
Rules(..),
|
||||||
Port,
|
|
||||||
ConnectionState(..)
|
ConnectionState(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
@ -18,7 +17,6 @@ import Data.Char
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Network as Network
|
import qualified Propellor.Property.Network as Network
|
||||||
|
|
||||||
|
@ -46,8 +44,8 @@ toIpTable r = map Param $
|
||||||
toIpTableArg :: Rules -> [String]
|
toIpTableArg :: Rules -> [String]
|
||||||
toIpTableArg Everything = []
|
toIpTableArg Everything = []
|
||||||
toIpTableArg (Proto proto) = ["-p", map toLower $ show proto]
|
toIpTableArg (Proto proto) = ["-p", map toLower $ show proto]
|
||||||
toIpTableArg (Port port) = ["--dport", show port]
|
toIpTableArg (DPort port) = ["--dport", show port]
|
||||||
toIpTableArg (PortRange (f,t)) = ["--dport", show f ++ ":" ++ show t]
|
toIpTableArg (DPortRange (f,t)) = ["--dport", show f ++ ":" ++ show t]
|
||||||
toIpTableArg (IFace iface) = ["-i", iface]
|
toIpTableArg (IFace iface) = ["-i", iface]
|
||||||
toIpTableArg (Ctstate states) = ["-m", "conntrack","--ctstate", concat $ intersperse "," (map show states)]
|
toIpTableArg (Ctstate states) = ["-m", "conntrack","--ctstate", concat $ intersperse "," (map show states)]
|
||||||
toIpTableArg (r :- r') = toIpTableArg r <> toIpTableArg r'
|
toIpTableArg (r :- r') = toIpTableArg r <> toIpTableArg r'
|
||||||
|
@ -56,33 +54,31 @@ data Rule = Rule
|
||||||
{ ruleChain :: Chain
|
{ ruleChain :: Chain
|
||||||
, ruleTarget :: Target
|
, ruleTarget :: Target
|
||||||
, ruleRules :: Rules
|
, ruleRules :: Rules
|
||||||
} deriving (Eq, Show, Read)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
data Chain = INPUT | OUTPUT | FORWARD
|
data Chain = INPUT | OUTPUT | FORWARD
|
||||||
deriving (Eq,Show,Read)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
data Target = ACCEPT | REJECT | DROP | LOG
|
data Target = ACCEPT | REJECT | DROP | LOG
|
||||||
deriving (Eq,Show,Read)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
data Proto = TCP | UDP | ICMP
|
data Proto = TCP | UDP | ICMP
|
||||||
deriving (Eq,Show,Read)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
type Port = Int
|
|
||||||
|
|
||||||
data ConnectionState = ESTABLISHED | RELATED | NEW | INVALID
|
data ConnectionState = ESTABLISHED | RELATED | NEW | INVALID
|
||||||
deriving (Eq,Show,Read)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
data Rules
|
data Rules
|
||||||
= Everything
|
= Everything
|
||||||
| Proto Proto
|
| Proto Proto
|
||||||
-- ^There is actually some order dependency between proto and port so this should be a specific
|
-- ^There is actually some order dependency between proto and port so this should be a specific
|
||||||
-- data type with proto + ports
|
-- data type with proto + ports
|
||||||
| Port Port
|
| DPort Port
|
||||||
| PortRange (Port,Port)
|
| DPortRange (Port,Port)
|
||||||
| IFace Network.Interface
|
| IFace Network.Interface
|
||||||
| Ctstate [ ConnectionState ]
|
| Ctstate [ ConnectionState ]
|
||||||
| Rules :- Rules -- ^Combine two rules
|
| Rules :- Rules -- ^Combine two rules
|
||||||
deriving (Eq,Show,Read)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
infixl 0 :-
|
infixl 0 :-
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import Propellor
|
||||||
import Propellor.Property.File
|
import Propellor.Property.File
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Service as Service
|
import qualified Propellor.Property.Service as Service
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,33 @@
|
||||||
module Propellor.Property.Mount where
|
module Propellor.Property.Mount where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
import Utility.Path
|
||||||
|
|
||||||
type FsType = String
|
type FsType = String
|
||||||
type Source = String
|
type Source = String
|
||||||
|
|
||||||
|
-- | Lists all mount points of the system.
|
||||||
mountPoints :: IO [FilePath]
|
mountPoints :: IO [FilePath]
|
||||||
mountPoints = lines <$> readProcess "findmnt" ["-rn", "--output", "target"]
|
mountPoints = lines <$> readProcess "findmnt" ["-rn", "--output", "target"]
|
||||||
|
|
||||||
|
-- | Finds all filesystems mounted inside the specified directory.
|
||||||
|
mountPointsBelow :: FilePath -> IO [FilePath]
|
||||||
|
mountPointsBelow target = filter (\p -> simplifyPath p /= simplifyPath target)
|
||||||
|
. filter (dirContains target)
|
||||||
|
<$> mountPoints
|
||||||
|
|
||||||
|
-- | Filesystem type mounted at a given location.
|
||||||
getFsType :: FilePath -> IO (Maybe FsType)
|
getFsType :: FilePath -> IO (Maybe FsType)
|
||||||
getFsType mnt = catchDefaultIO Nothing $
|
getFsType mnt = catchDefaultIO Nothing $
|
||||||
headMaybe . lines
|
headMaybe . lines
|
||||||
<$> readProcess "findmnt" ["-n", mnt, "--output", "fstype"]
|
<$> readProcess "findmnt" ["-n", mnt, "--output", "fstype"]
|
||||||
|
|
||||||
|
-- | Unmounts a device, lazily so any running processes don't block it.
|
||||||
umountLazy :: FilePath -> IO ()
|
umountLazy :: FilePath -> IO ()
|
||||||
umountLazy mnt =
|
umountLazy mnt =
|
||||||
unlessM (boolSystem "umount" [ Param "-l", Param mnt ]) $
|
unlessM (boolSystem "umount" [ Param "-l", Param mnt ]) $
|
||||||
errorMessage $ "failed unmounting " ++ mnt
|
errorMessage $ "failed unmounting " ++ mnt
|
||||||
|
|
||||||
|
-- | Mounts a device.
|
||||||
mount :: FsType -> Source -> FilePath -> IO Bool
|
mount :: FsType -> Source -> FilePath -> IO Bool
|
||||||
mount fs src mnt = boolSystem "mount" [Param "-t", Param fs, Param src, Param mnt]
|
mount fs src mnt = boolSystem "mount" [Param "-t", Param fs, Param src, Param mnt]
|
||||||
|
|
|
@ -16,7 +16,6 @@ import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Reboot as Reboot
|
import qualified Propellor.Property.Reboot as Reboot
|
||||||
import Propellor.Property.Mount
|
import Propellor.Property.Mount
|
||||||
import Propellor.Property.Chroot.Util (stdPATH)
|
import Propellor.Property.Chroot.Util (stdPATH)
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
import System.Posix.Files (rename, fileExist)
|
import System.Posix.Files (rename, fileExist)
|
||||||
import Control.Exception (throw)
|
import Control.Exception (throw)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import Propellor
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Cron as Cron
|
import qualified Propellor.Property.Cron as Cron
|
||||||
import qualified Propellor.Property.Gpg as Gpg
|
import qualified Propellor.Property.Gpg as Gpg
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,11 @@ reloaded :: Property NoInfo
|
||||||
reloaded = Service.reloaded "postfix"
|
reloaded = Service.reloaded "postfix"
|
||||||
|
|
||||||
-- | Configures postfix as a satellite system, which
|
-- | Configures postfix as a satellite system, which
|
||||||
-- relays all mail through a relay host, which defaults to smtp.domain.
|
-- relays all mail through a relay host, which defaults to smtp.domain,
|
||||||
|
-- but can be changed by @mainCf "relayhost"@.
|
||||||
--
|
--
|
||||||
-- The smarthost may refuse to relay mail on to other domains, without
|
-- The smarthost may refuse to relay mail on to other domains, without
|
||||||
-- futher coniguration/keys. But this should be enough to get cron job
|
-- further configuration/keys. But this should be enough to get cron job
|
||||||
-- mail flowing to a place where it will be seen.
|
-- mail flowing to a place where it will be seen.
|
||||||
satellite :: Property NoInfo
|
satellite :: Property NoInfo
|
||||||
satellite = check (not <$> mainCfIsSet "relayhost") setup
|
satellite = check (not <$> mainCfIsSet "relayhost") setup
|
||||||
|
@ -34,14 +35,14 @@ satellite = check (not <$> mainCfIsSet "relayhost") setup
|
||||||
setup = trivial $ property "postfix satellite system" $ do
|
setup = trivial $ property "postfix satellite system" $ do
|
||||||
hn <- asks hostName
|
hn <- asks hostName
|
||||||
let (_, domain) = separate (== '.') hn
|
let (_, domain) = separate (== '.') hn
|
||||||
ensureProperties
|
ensureProperties
|
||||||
[ Apt.reConfigure "postfix"
|
[ Apt.reConfigure "postfix"
|
||||||
[ ("postfix/main_mailer_type", "select", "Satellite system")
|
[ ("postfix/main_mailer_type", "select", "Satellite system")
|
||||||
, ("postfix/root_address", "string", "root")
|
, ("postfix/root_address", "string", "root")
|
||||||
, ("postfix/destinations", "string", "localhost")
|
, ("postfix/destinations", "string", "localhost")
|
||||||
, ("postfix/mailname", "string", hn)
|
, ("postfix/mailname", "string", hn)
|
||||||
]
|
]
|
||||||
, mainCf ("relayhost", domain)
|
, mainCf ("relayhost", "smtp." ++ domain)
|
||||||
`onChange` reloaded
|
`onChange` reloaded
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ mappedFile f setup = setup f
|
||||||
`onChange` cmdProperty "postmap" [f]
|
`onChange` cmdProperty "postmap" [f]
|
||||||
|
|
||||||
-- | Run newaliases command, which should be done after changing
|
-- | Run newaliases command, which should be done after changing
|
||||||
-- </etc/aliases>.
|
-- @/etc/aliases@.
|
||||||
newaliases :: Property NoInfo
|
newaliases :: Property NoInfo
|
||||||
newaliases = trivial $ cmdProperty "newaliases" []
|
newaliases = trivial $ cmdProperty "newaliases" []
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ newaliases = trivial $ cmdProperty "newaliases" []
|
||||||
mainCfFile :: FilePath
|
mainCfFile :: FilePath
|
||||||
mainCfFile = "/etc/postfix/main.cf"
|
mainCfFile = "/etc/postfix/main.cf"
|
||||||
|
|
||||||
-- | Sets a main.cf name=value pair. Does not reload postfix immediately.
|
-- | Sets a main.cf @name=value@ pair. Does not reload postfix immediately.
|
||||||
mainCf :: (String, String) -> Property NoInfo
|
mainCf :: (String, String) -> Property NoInfo
|
||||||
mainCf (name, value) = check notset set
|
mainCf (name, value) = check notset set
|
||||||
`describe` ("postfix main.cf " ++ setting)
|
`describe` ("postfix main.cf " ++ setting)
|
||||||
|
@ -74,7 +75,7 @@ mainCf (name, value) = check notset set
|
||||||
notset = (/= Just value) <$> getMainCf name
|
notset = (/= Just value) <$> getMainCf name
|
||||||
set = cmdProperty "postconf" ["-e", setting]
|
set = cmdProperty "postconf" ["-e", setting]
|
||||||
|
|
||||||
-- | Gets a man.cf setting.
|
-- | Gets a main.cf setting.
|
||||||
getMainCf :: String -> IO (Maybe String)
|
getMainCf :: String -> IO (Maybe String)
|
||||||
getMainCf name = parse . lines <$> readProcess "postconf" [name]
|
getMainCf name = parse . lines <$> readProcess "postconf" [name]
|
||||||
where
|
where
|
||||||
|
@ -130,9 +131,9 @@ dedupCf ls =
|
||||||
-- | Installs saslauthd and configures it for postfix, authenticating
|
-- | Installs saslauthd and configures it for postfix, authenticating
|
||||||
-- against PAM.
|
-- against PAM.
|
||||||
--
|
--
|
||||||
-- Does not configure postfix to use it; eg smtpd_sasl_auth_enable = yes
|
-- Does not configure postfix to use it; eg @smtpd_sasl_auth_enable = yes@
|
||||||
-- needs to be set to enable use. See
|
-- needs to be set to enable use. See
|
||||||
-- https://wiki.debian.org/PostfixAndSASL
|
-- <https://wiki.debian.org/PostfixAndSASL>.
|
||||||
saslAuthdInstalled :: Property NoInfo
|
saslAuthdInstalled :: Property NoInfo
|
||||||
saslAuthdInstalled = setupdaemon
|
saslAuthdInstalled = setupdaemon
|
||||||
`requires` Service.running "saslauthd"
|
`requires` Service.running "saslauthd"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module Propellor.Property.Reboot where
|
module Propellor.Property.Reboot where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
now :: Property NoInfo
|
now :: Property NoInfo
|
||||||
now = cmdProperty "reboot" []
|
now = cmdProperty "reboot" []
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module Propellor.Property.Service where
|
module Propellor.Property.Service where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
type ServiceName = String
|
type ServiceName = String
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import Propellor
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.User as User
|
import qualified Propellor.Property.User as User
|
||||||
import qualified Propellor.Property.Cron as Cron
|
import qualified Propellor.Property.Cron as Cron
|
||||||
import qualified Propellor.Property.Ssh as Ssh
|
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Docker as Docker
|
import qualified Propellor.Property.Systemd as Systemd
|
||||||
|
import qualified Propellor.Property.Chroot as Chroot
|
||||||
import Propellor.Property.Cron (Times)
|
import Propellor.Property.Cron (Times)
|
||||||
|
|
||||||
builduser :: UserName
|
builduser :: UserName
|
||||||
|
@ -48,8 +48,6 @@ autobuilder arch crontimes timeout = combineProperties "gitannexbuilder" $ props
|
||||||
tree :: Architecture -> Property HasInfo
|
tree :: Architecture -> Property HasInfo
|
||||||
tree buildarch = combineProperties "gitannexbuilder tree" $ props
|
tree buildarch = combineProperties "gitannexbuilder tree" $ props
|
||||||
& Apt.installed ["git"]
|
& Apt.installed ["git"]
|
||||||
-- gitbuilderdir directory already exists when docker volume is used,
|
|
||||||
-- but with wrong owner.
|
|
||||||
& File.dirExists gitbuilderdir
|
& File.dirExists gitbuilderdir
|
||||||
& File.ownerGroup gitbuilderdir (User builduser) (Group builduser)
|
& File.ownerGroup gitbuilderdir (User builduser) (Group builduser)
|
||||||
& gitannexbuildercloned
|
& gitannexbuildercloned
|
||||||
|
@ -69,7 +67,6 @@ tree buildarch = combineProperties "gitannexbuilder tree" $ props
|
||||||
buildDepsApt :: Property HasInfo
|
buildDepsApt :: Property HasInfo
|
||||||
buildDepsApt = combineProperties "gitannexbuilder build deps" $ props
|
buildDepsApt = combineProperties "gitannexbuilder build deps" $ props
|
||||||
& Apt.buildDep ["git-annex"]
|
& Apt.buildDep ["git-annex"]
|
||||||
& Apt.installed ["liblockfile-simple-perl"]
|
|
||||||
& buildDepsNoHaskellLibs
|
& buildDepsNoHaskellLibs
|
||||||
& Apt.buildDepIn builddir
|
& Apt.buildDepIn builddir
|
||||||
`describe` "git-annex source build deps installed"
|
`describe` "git-annex source build deps installed"
|
||||||
|
@ -84,6 +81,13 @@ buildDepsNoHaskellLibs = Apt.installed
|
||||||
"alex", "happy", "c2hs"
|
"alex", "happy", "c2hs"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
haskellPkgsInstalled :: String -> Property NoInfo
|
||||||
|
haskellPkgsInstalled dir = flagFile go ("/haskellpkgsinstalled")
|
||||||
|
where
|
||||||
|
go = userScriptProperty (User builduser)
|
||||||
|
[ "cd " ++ builddir ++ " && ./standalone/" ++ dir ++ "/install-haskell-packages"
|
||||||
|
]
|
||||||
|
|
||||||
-- Installs current versions of git-annex's deps from cabal, but only
|
-- Installs current versions of git-annex's deps from cabal, but only
|
||||||
-- does so once.
|
-- does so once.
|
||||||
cabalDeps :: Property NoInfo
|
cabalDeps :: Property NoInfo
|
||||||
|
@ -92,46 +96,60 @@ cabalDeps = flagFile go cabalupdated
|
||||||
go = userScriptProperty (User builduser) ["cabal update && cabal install git-annex --only-dependencies || true"]
|
go = userScriptProperty (User builduser) ["cabal update && cabal install git-annex --only-dependencies || true"]
|
||||||
cabalupdated = homedir </> ".cabal" </> "packages" </> "hackage.haskell.org" </> "00-index.cache"
|
cabalupdated = homedir </> ".cabal" </> "packages" </> "hackage.haskell.org" </> "00-index.cache"
|
||||||
|
|
||||||
standardAutoBuilderContainer :: (System -> Docker.Image) -> Architecture -> Int -> TimeOut -> Docker.Container
|
autoBuilderContainer :: (System -> Property HasInfo) -> System -> Times -> TimeOut -> Systemd.Container
|
||||||
standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.container (arch ++ "-git-annex-builder")
|
autoBuilderContainer mkprop osver@(System _ arch) crontime timeout =
|
||||||
(dockerImage $ System (Debian Testing) arch)
|
Systemd.container name bootstrap
|
||||||
& os (System (Debian Testing) arch)
|
& mkprop osver
|
||||||
& Apt.stdSourcesList
|
& buildDepsApt
|
||||||
& Apt.installed ["systemd"]
|
& autobuilder arch crontime timeout
|
||||||
& Apt.unattendedUpgrades
|
where
|
||||||
& User.accountFor (User builduser)
|
name = arch ++ "-git-annex-builder"
|
||||||
& tree arch
|
bootstrap = Chroot.debootstrapped osver mempty
|
||||||
& buildDepsApt
|
|
||||||
& autobuilder arch (Cron.Times $ show buildminute ++ " * * * *") timeout
|
|
||||||
& Docker.tweaked
|
|
||||||
|
|
||||||
androidAutoBuilderContainer :: (System -> Docker.Image) -> Times -> TimeOut -> Docker.Container
|
standardAutoBuilder :: System -> Property HasInfo
|
||||||
androidAutoBuilderContainer dockerImage crontimes timeout =
|
standardAutoBuilder osver@(System _ arch) =
|
||||||
androidContainer dockerImage "android-git-annex-builder" (tree "android") builddir
|
propertyList "standard git-annex autobuilder" $ props
|
||||||
|
& os osver
|
||||||
|
& Apt.stdSourcesList
|
||||||
|
& Apt.unattendedUpgrades
|
||||||
|
& User.accountFor (User builduser)
|
||||||
|
& tree arch
|
||||||
|
|
||||||
|
armAutoBuilder :: System -> Times -> TimeOut -> Property HasInfo
|
||||||
|
armAutoBuilder osver@(System _ arch) crontime timeout =
|
||||||
|
propertyList "arm git-annex autobuilder" $ props
|
||||||
|
& standardAutoBuilder osver
|
||||||
|
& buildDepsNoHaskellLibs
|
||||||
|
-- Works around ghc crash with parallel builds on arm.
|
||||||
|
& (homedir </> ".cabal" </> "config")
|
||||||
|
`File.lacksLine` "jobs: $ncpus"
|
||||||
|
-- Install patched haskell packages for portability to
|
||||||
|
-- arm NAS's using old kernel versions.
|
||||||
|
& haskellPkgsInstalled "linux"
|
||||||
|
& autobuilder arch crontime timeout
|
||||||
|
|
||||||
|
androidAutoBuilderContainer :: Times -> TimeOut -> Systemd.Container
|
||||||
|
androidAutoBuilderContainer crontimes timeout =
|
||||||
|
androidContainer "android-git-annex-builder" (tree "android") builddir
|
||||||
& Apt.unattendedUpgrades
|
& Apt.unattendedUpgrades
|
||||||
& autobuilder "android" crontimes timeout
|
& autobuilder "android" crontimes timeout
|
||||||
|
|
||||||
-- Android is cross-built in a Debian i386 container, using the Android NDK.
|
-- Android is cross-built in a Debian i386 container, using the Android NDK.
|
||||||
androidContainer
|
androidContainer
|
||||||
:: (IsProp (Property (CInfo NoInfo i)), (Combines (Property NoInfo) (Property i)))
|
:: (IsProp (Property (CInfo NoInfo i)), (Combines (Property NoInfo) (Property i)))
|
||||||
=> (System -> Docker.Image)
|
=> Systemd.MachineName
|
||||||
-> Docker.ContainerName
|
|
||||||
-> Property i
|
-> Property i
|
||||||
-> FilePath
|
-> FilePath
|
||||||
-> Docker.Container
|
-> Systemd.Container
|
||||||
androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.container name
|
androidContainer name setupgitannexdir gitannexdir = Systemd.container name bootstrap
|
||||||
(dockerImage osver)
|
|
||||||
& os osver
|
& os osver
|
||||||
& Apt.stdSourcesList
|
& Apt.stdSourcesList
|
||||||
& Apt.installed ["systemd"]
|
|
||||||
& Docker.tweaked
|
|
||||||
& User.accountFor (User builduser)
|
& User.accountFor (User builduser)
|
||||||
& File.dirExists gitbuilderdir
|
& File.dirExists gitbuilderdir
|
||||||
& File.ownerGroup homedir (User builduser) (Group builduser)
|
& File.ownerGroup homedir (User builduser) (Group builduser)
|
||||||
& buildDepsApt
|
|
||||||
& flagFile chrootsetup ("/chrootsetup")
|
& flagFile chrootsetup ("/chrootsetup")
|
||||||
`requires` setupgitannexdir
|
`requires` setupgitannexdir
|
||||||
& flagFile haskellpkgsinstalled ("/haskellpkgsinstalled")
|
& haskellPkgsInstalled "android"
|
||||||
where
|
where
|
||||||
-- Use git-annex's android chroot setup script, which will install
|
-- Use git-annex's android chroot setup script, which will install
|
||||||
-- ghc-android and the NDK, all build deps, etc, in the home
|
-- ghc-android and the NDK, all build deps, etc, in the home
|
||||||
|
@ -139,54 +157,5 @@ androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.containe
|
||||||
chrootsetup = scriptProperty
|
chrootsetup = scriptProperty
|
||||||
[ "cd " ++ gitannexdir ++ " && ./standalone/android/buildchroot-inchroot"
|
[ "cd " ++ gitannexdir ++ " && ./standalone/android/buildchroot-inchroot"
|
||||||
]
|
]
|
||||||
haskellpkgsinstalled = userScriptProperty (User builduser)
|
osver = System (Debian (Stable "jessie")) "i386"
|
||||||
[ "cd " ++ gitannexdir ++ " && ./standalone/android/install-haskell-packages"
|
bootstrap = Chroot.debootstrapped osver mempty
|
||||||
]
|
|
||||||
osver = System (Debian Testing) "i386"
|
|
||||||
|
|
||||||
-- armel builder has a companion container using amd64 that
|
|
||||||
-- runs the build first to get TH splices. They need
|
|
||||||
-- to have the same versions of all haskell libraries installed.
|
|
||||||
armelCompanionContainer :: (System -> Docker.Image) -> Docker.Container
|
|
||||||
armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder-companion"
|
|
||||||
(dockerImage $ System (Debian Unstable) "amd64")
|
|
||||||
& os (System (Debian Testing) "amd64")
|
|
||||||
& Apt.stdSourcesList
|
|
||||||
& Apt.installed ["systemd"]
|
|
||||||
-- This volume is shared with the armel builder.
|
|
||||||
& Docker.volume gitbuilderdir
|
|
||||||
& User.accountFor (User builduser)
|
|
||||||
-- Install current versions of build deps from cabal.
|
|
||||||
& tree "armel"
|
|
||||||
& buildDepsNoHaskellLibs
|
|
||||||
& cabalDeps
|
|
||||||
-- The armel builder can ssh to this companion.
|
|
||||||
& Docker.expose "22"
|
|
||||||
& Apt.serviceInstalledRunning "ssh"
|
|
||||||
& Ssh.authorizedKeys (User builduser) (Context "armel-git-annex-builder")
|
|
||||||
& Docker.tweaked
|
|
||||||
|
|
||||||
armelAutoBuilderContainer :: (System -> Docker.Image) -> Times -> TimeOut -> Docker.Container
|
|
||||||
armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder"
|
|
||||||
(dockerImage $ System (Debian Unstable) "armel")
|
|
||||||
& os (System (Debian Testing) "armel")
|
|
||||||
& Apt.stdSourcesList
|
|
||||||
& Apt.installed ["systemd"]
|
|
||||||
& Apt.installed ["openssh-client"]
|
|
||||||
& Docker.link "armel-git-annex-builder-companion" "companion"
|
|
||||||
& Docker.volumes_from "armel-git-annex-builder-companion"
|
|
||||||
& User.accountFor (User builduser)
|
|
||||||
-- TODO: automate installing haskell libs
|
|
||||||
-- (Currently have to run
|
|
||||||
-- git-annex/standalone/linux/install-haskell-packages
|
|
||||||
-- which is not fully automated.)
|
|
||||||
& buildDepsNoHaskellLibs
|
|
||||||
& autobuilder "armel" crontimes timeout
|
|
||||||
`requires` tree "armel"
|
|
||||||
& Ssh.keyImported SshRsa (User builduser) (Context "armel-git-annex-builder")
|
|
||||||
& trivial writecompanionaddress
|
|
||||||
& Docker.tweaked
|
|
||||||
where
|
|
||||||
writecompanionaddress = scriptProperty
|
|
||||||
[ "echo \"$COMPANION_PORT_22_TCP_ADDR\" > " ++ homedir </> "companion_address"
|
|
||||||
] `describe` "companion_address file"
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ module Propellor.Property.SiteSpecific.GitHome where
|
||||||
import Propellor
|
import Propellor
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import Propellor.Property.User
|
import Propellor.Property.User
|
||||||
import Utility.SafeCommand
|
|
||||||
|
|
||||||
-- | Clones Joey Hess's git home directory, and runs its fixups script.
|
-- | Clones Joey Hess's git home directory, and runs its fixups script.
|
||||||
installedFor :: User -> Property NoInfo
|
installedFor :: User -> Property NoInfo
|
||||||
|
|
|
@ -35,7 +35,7 @@ gitServer knownhosts = propertyList "iabak git server" $ props
|
||||||
& Cron.niceJob "shardstats" (Cron.Times "*/30 * * * *") (User "root") "/"
|
& Cron.niceJob "shardstats" (Cron.Times "*/30 * * * *") (User "root") "/"
|
||||||
"/usr/local/IA.BAK/shardstats-all"
|
"/usr/local/IA.BAK/shardstats-all"
|
||||||
& Cron.niceJob "shardmaint" Cron.Daily (User "root") "/"
|
& Cron.niceJob "shardmaint" Cron.Daily (User "root") "/"
|
||||||
"/usr/local/IA.BAK/shardmaint"
|
"/usr/local/IA.BAK/shardmaint-fast; /usr/local/IA.BAK/shardmaint"
|
||||||
|
|
||||||
registrationServer :: [Host] -> Property HasInfo
|
registrationServer :: [Host] -> Property HasInfo
|
||||||
registrationServer knownhosts = propertyList "iabak registration server" $ props
|
registrationServer knownhosts = propertyList "iabak registration server" $ props
|
||||||
|
@ -64,14 +64,13 @@ graphiteServer = propertyList "iabak graphite server" $ props
|
||||||
, "pattern = ^carbon\\."
|
, "pattern = ^carbon\\."
|
||||||
, "retentions = 60:90d"
|
, "retentions = 60:90d"
|
||||||
, "[iabak-connections]"
|
, "[iabak-connections]"
|
||||||
, "pattern = ^iabak\.shardstats\.connections"
|
, "pattern = ^iabak\\.shardstats\\.connections"
|
||||||
, "retentions = 1h:1y,3h:10y"
|
, "retentions = 1h:1y,3h:10y"
|
||||||
, "[iabak]"
|
, "[iabak-default]"
|
||||||
, "pattern = ^iabak\\."
|
, "pattern = ^iabak\\."
|
||||||
, "retentions = 10m:30d,1h:1y,3h:10y"
|
, "retentions = 10m:30d,1h:1y,3h:10y"
|
||||||
, "[default_1min_for_1day]"
|
, "[default_1min_for_1day]"
|
||||||
, "pattern = .*"
|
, "pattern = .*"
|
||||||
, "retentions = 60s:1d"
|
|
||||||
]
|
]
|
||||||
& graphiteCSRF
|
& graphiteCSRF
|
||||||
& cmdProperty "graphite-manage" ["syncdb", "--noinput"] `flagFile` "/etc/flagFiles/graphite-syncdb"
|
& cmdProperty "graphite-manage" ["syncdb", "--noinput"] `flagFile` "/etc/flagFiles/graphite-syncdb"
|
||||||
|
|
|
@ -15,7 +15,6 @@ import qualified Propellor.Property.User as User
|
||||||
import qualified Propellor.Property.Obnam as Obnam
|
import qualified Propellor.Property.Obnam as Obnam
|
||||||
import qualified Propellor.Property.Apache as Apache
|
import qualified Propellor.Property.Apache as Apache
|
||||||
import qualified Propellor.Property.Postfix as Postfix
|
import qualified Propellor.Property.Postfix as Postfix
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
@ -30,7 +29,6 @@ scrollBox = propertyList "scroll server" $ props
|
||||||
"libghc-bytestring-dev", "libghc-mtl-dev", "libghc-ncurses-dev",
|
"libghc-bytestring-dev", "libghc-mtl-dev", "libghc-ncurses-dev",
|
||||||
"libghc-random-dev", "libghc-monad-loops-dev", "libghc-text-dev",
|
"libghc-random-dev", "libghc-monad-loops-dev", "libghc-text-dev",
|
||||||
"libghc-ifelse-dev", "libghc-case-insensitive-dev",
|
"libghc-ifelse-dev", "libghc-case-insensitive-dev",
|
||||||
"libghc-transformers-dev",
|
|
||||||
"libghc-data-default-dev", "libghc-optparse-applicative-dev"]
|
"libghc-data-default-dev", "libghc-optparse-applicative-dev"]
|
||||||
& userScriptProperty (User "scroll")
|
& userScriptProperty (User "scroll")
|
||||||
[ "cd " ++ d </> "scroll"
|
[ "cd " ++ d </> "scroll"
|
||||||
|
@ -389,7 +387,7 @@ twitRss = combineProperties "twitter rss" $ props
|
||||||
-- Work around for expired ssl cert.
|
-- Work around for expired ssl cert.
|
||||||
pumpRss :: Property NoInfo
|
pumpRss :: Property NoInfo
|
||||||
pumpRss = Cron.job "pump rss" (Cron.Times "15 * * * *") (User "joey") "/srv/web/tmp.kitenet.net/"
|
pumpRss = Cron.job "pump rss" (Cron.Times "15 * * * *") (User "joey") "/srv/web/tmp.kitenet.net/"
|
||||||
"wget https://pump2rss.com/feed/joeyh@identi.ca.atom -O pump.atom.new --no-check-certificate 2>/dev/null; sed 's/ & / /g' pump.atom.new > pump.atom"
|
"wget https://rss.io.jpope.org/feed/joeyh@identi.ca.atom -O pump.atom.new --no-check-certificate 2>/dev/null; sed 's/ & / /g' pump.atom.new > pump.atom"
|
||||||
|
|
||||||
ircBouncer :: Property HasInfo
|
ircBouncer :: Property HasInfo
|
||||||
ircBouncer = propertyList "IRC bouncer" $ props
|
ircBouncer = propertyList "IRC bouncer" $ props
|
||||||
|
@ -407,7 +405,7 @@ ircBouncer = propertyList "IRC bouncer" $ props
|
||||||
|
|
||||||
kiteShellBox :: Property NoInfo
|
kiteShellBox :: Property NoInfo
|
||||||
kiteShellBox = propertyList "kitenet.net shellinabox"
|
kiteShellBox = propertyList "kitenet.net shellinabox"
|
||||||
[ Apt.installed ["openssl", "shellinabox"]
|
[ Apt.installed ["openssl", "shellinabox", "openssh-client"]
|
||||||
, File.hasContent "/etc/default/shellinabox"
|
, File.hasContent "/etc/default/shellinabox"
|
||||||
[ "# Deployed by propellor"
|
[ "# Deployed by propellor"
|
||||||
, "SHELLINABOX_DAEMON_START=1"
|
, "SHELLINABOX_DAEMON_START=1"
|
||||||
|
@ -861,6 +859,8 @@ legacyWebSites = propertyList "legacy web sites" $ props
|
||||||
, " AllowOverride None"
|
, " AllowOverride None"
|
||||||
, Apache.allowAll
|
, Apache.allowAll
|
||||||
, "</Directory>"
|
, "</Directory>"
|
||||||
|
, "RewriteEngine On"
|
||||||
|
, "RewriteRule .* http://www.sowsearpoetry.org/ [L]"
|
||||||
]
|
]
|
||||||
& alias "wortroot.kitenet.net"
|
& alias "wortroot.kitenet.net"
|
||||||
& alias "www.wortroot.kitenet.net"
|
& alias "www.wortroot.kitenet.net"
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
module Propellor.Property.Ssh (
|
module Propellor.Property.Ssh (
|
||||||
PubKeyText,
|
PubKeyText,
|
||||||
sshdConfig,
|
sshdConfig,
|
||||||
|
ConfigKeyword,
|
||||||
|
setSshdConfigBool,
|
||||||
setSshdConfig,
|
setSshdConfig,
|
||||||
|
RootLogin(..),
|
||||||
permitRootLogin,
|
permitRootLogin,
|
||||||
passwordAuthentication,
|
passwordAuthentication,
|
||||||
noPasswords,
|
noPasswords,
|
||||||
|
@ -24,11 +27,11 @@ import Propellor
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import qualified Propellor.Property.Service as Service
|
import qualified Propellor.Property.Service as Service
|
||||||
import Propellor.Property.User
|
import Propellor.Property.User
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
import System.PosixCompat
|
import System.PosixCompat
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import Data.List
|
||||||
|
|
||||||
type PubKeyText = String
|
type PubKeyText = String
|
||||||
|
|
||||||
|
@ -39,21 +42,37 @@ sshBool False = "no"
|
||||||
sshdConfig :: FilePath
|
sshdConfig :: FilePath
|
||||||
sshdConfig = "/etc/ssh/sshd_config"
|
sshdConfig = "/etc/ssh/sshd_config"
|
||||||
|
|
||||||
setSshdConfig :: String -> Bool -> Property NoInfo
|
type ConfigKeyword = String
|
||||||
setSshdConfig setting allowed = combineProperties "sshd config"
|
|
||||||
[ sshdConfig `File.lacksLine` (sshline $ not allowed)
|
|
||||||
, sshdConfig `File.containsLine` (sshline allowed)
|
|
||||||
]
|
|
||||||
`onChange` restarted
|
|
||||||
`describe` unwords [ "ssh config:", setting, sshBool allowed ]
|
|
||||||
where
|
|
||||||
sshline v = setting ++ " " ++ sshBool v
|
|
||||||
|
|
||||||
permitRootLogin :: Bool -> Property NoInfo
|
setSshdConfigBool :: ConfigKeyword -> Bool -> Property NoInfo
|
||||||
permitRootLogin = setSshdConfig "PermitRootLogin"
|
setSshdConfigBool setting allowed = setSshdConfig setting (sshBool allowed)
|
||||||
|
|
||||||
|
setSshdConfig :: ConfigKeyword -> String -> Property NoInfo
|
||||||
|
setSshdConfig setting val = File.fileProperty desc f sshdConfig
|
||||||
|
`onChange` restarted
|
||||||
|
where
|
||||||
|
desc = unwords [ "ssh config:", setting, val ]
|
||||||
|
cfgline = setting ++ " " ++ val
|
||||||
|
wantedline s
|
||||||
|
| s == cfgline = True
|
||||||
|
| (setting ++ " ") `isPrefixOf` s = False
|
||||||
|
| otherwise = True
|
||||||
|
f ls
|
||||||
|
| cfgline `elem` ls = filter wantedline ls
|
||||||
|
| otherwise = filter wantedline ls ++ [cfgline]
|
||||||
|
|
||||||
|
data RootLogin
|
||||||
|
= RootLogin Bool -- ^ allow or prevent root login
|
||||||
|
| WithoutPassword -- ^ disable password authentication for root, while allowing other authentication methods
|
||||||
|
| ForcedCommandsOnly -- ^ allow root login with public-key authentication, but only if a forced command has been specified for the public key
|
||||||
|
|
||||||
|
permitRootLogin :: RootLogin -> Property NoInfo
|
||||||
|
permitRootLogin (RootLogin b) = setSshdConfigBool "PermitRootLogin" b
|
||||||
|
permitRootLogin WithoutPassword = setSshdConfig "PermitRootLogin" "without-password"
|
||||||
|
permitRootLogin ForcedCommandsOnly = setSshdConfig "PermitRootLogin" "forced-commands-only"
|
||||||
|
|
||||||
passwordAuthentication :: Bool -> Property NoInfo
|
passwordAuthentication :: Bool -> Property NoInfo
|
||||||
passwordAuthentication = setSshdConfig "PasswordAuthentication"
|
passwordAuthentication = setSshdConfigBool "PasswordAuthentication"
|
||||||
|
|
||||||
-- | Configure ssh to not allow password logins.
|
-- | Configure ssh to not allow password logins.
|
||||||
--
|
--
|
||||||
|
|
|
@ -1,31 +1,51 @@
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
|
||||||
module Propellor.Property.Systemd (
|
module Propellor.Property.Systemd (
|
||||||
module Propellor.Property.Systemd.Core,
|
-- * Services
|
||||||
ServiceName,
|
ServiceName,
|
||||||
MachineName,
|
|
||||||
started,
|
started,
|
||||||
stopped,
|
stopped,
|
||||||
enabled,
|
enabled,
|
||||||
disabled,
|
disabled,
|
||||||
|
masked,
|
||||||
|
running,
|
||||||
restarted,
|
restarted,
|
||||||
persistentJournal,
|
networkd,
|
||||||
|
journald,
|
||||||
|
-- * Configuration
|
||||||
|
installed,
|
||||||
Option,
|
Option,
|
||||||
configured,
|
configured,
|
||||||
journaldConfigured,
|
|
||||||
daemonReloaded,
|
daemonReloaded,
|
||||||
|
-- * Journal
|
||||||
|
persistentJournal,
|
||||||
|
journaldConfigured,
|
||||||
|
-- * Containers
|
||||||
|
MachineName,
|
||||||
Container,
|
Container,
|
||||||
container,
|
container,
|
||||||
nspawned,
|
nspawned,
|
||||||
|
-- * Container configuration
|
||||||
containerCfg,
|
containerCfg,
|
||||||
resolvConfed,
|
resolvConfed,
|
||||||
|
linkJournal,
|
||||||
|
privateNetwork,
|
||||||
|
module Propellor.Types.Container,
|
||||||
|
Proto(..),
|
||||||
|
Publishable,
|
||||||
|
publish,
|
||||||
|
Bindable,
|
||||||
|
bind,
|
||||||
|
bindRo,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Propellor.Types.Chroot
|
import Propellor.Types.Chroot
|
||||||
|
import Propellor.Types.Container
|
||||||
import qualified Propellor.Property.Chroot as Chroot
|
import qualified Propellor.Property.Chroot as Chroot
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.File as File
|
import qualified Propellor.Property.File as File
|
||||||
import Propellor.Property.Systemd.Core
|
import Propellor.Property.Systemd.Core
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
@ -45,6 +65,9 @@ instance PropAccum Container where
|
||||||
getProperties (Container _ _ h) = hostProperties h
|
getProperties (Container _ _ h) = hostProperties h
|
||||||
|
|
||||||
-- | Starts a systemd service.
|
-- | Starts a systemd service.
|
||||||
|
--
|
||||||
|
-- Note that this does not configure systemd to start the service on boot,
|
||||||
|
-- it only ensures that the service is currently running.
|
||||||
started :: ServiceName -> Property NoInfo
|
started :: ServiceName -> Property NoInfo
|
||||||
started n = trivial $ cmdProperty "systemctl" ["start", n]
|
started n = trivial $ cmdProperty "systemctl" ["start", n]
|
||||||
`describe` ("service " ++ n ++ " started")
|
`describe` ("service " ++ n ++ " started")
|
||||||
|
@ -55,6 +78,9 @@ stopped n = trivial $ cmdProperty "systemctl" ["stop", n]
|
||||||
`describe` ("service " ++ n ++ " stopped")
|
`describe` ("service " ++ n ++ " stopped")
|
||||||
|
|
||||||
-- | Enables a systemd service.
|
-- | Enables a systemd service.
|
||||||
|
--
|
||||||
|
-- This does not ensure the service is started, it only configures systemd
|
||||||
|
-- to start it on boot.
|
||||||
enabled :: ServiceName -> Property NoInfo
|
enabled :: ServiceName -> Property NoInfo
|
||||||
enabled n = trivial $ cmdProperty "systemctl" ["enable", n]
|
enabled n = trivial $ cmdProperty "systemctl" ["enable", n]
|
||||||
`describe` ("service " ++ n ++ " enabled")
|
`describe` ("service " ++ n ++ " enabled")
|
||||||
|
@ -64,11 +90,32 @@ disabled :: ServiceName -> Property NoInfo
|
||||||
disabled n = trivial $ cmdProperty "systemctl" ["disable", n]
|
disabled n = trivial $ cmdProperty "systemctl" ["disable", n]
|
||||||
`describe` ("service " ++ n ++ " disabled")
|
`describe` ("service " ++ n ++ " disabled")
|
||||||
|
|
||||||
|
-- | Masks a systemd service.
|
||||||
|
masked :: ServiceName -> RevertableProperty
|
||||||
|
masked n = systemdMask <!> systemdUnmask
|
||||||
|
where
|
||||||
|
systemdMask = trivial $ cmdProperty "systemctl" ["mask", n]
|
||||||
|
`describe` ("service " ++ n ++ " masked")
|
||||||
|
systemdUnmask = trivial $ cmdProperty "systemctl" ["unmask", n]
|
||||||
|
`describe` ("service " ++ n ++ " unmasked")
|
||||||
|
|
||||||
|
-- | Ensures that a service is both enabled and started
|
||||||
|
running :: ServiceName -> Property NoInfo
|
||||||
|
running n = trivial $ started n `requires` enabled n
|
||||||
|
|
||||||
-- | Restarts a systemd service.
|
-- | Restarts a systemd service.
|
||||||
restarted :: ServiceName -> Property NoInfo
|
restarted :: ServiceName -> Property NoInfo
|
||||||
restarted n = trivial $ cmdProperty "systemctl" ["restart", n]
|
restarted n = trivial $ cmdProperty "systemctl" ["restart", n]
|
||||||
`describe` ("service " ++ n ++ " restarted")
|
`describe` ("service " ++ n ++ " restarted")
|
||||||
|
|
||||||
|
-- | The systemd-networkd service.
|
||||||
|
networkd :: ServiceName
|
||||||
|
networkd = "systemd-networkd"
|
||||||
|
|
||||||
|
-- | The systemd-journald service.
|
||||||
|
journald :: ServiceName
|
||||||
|
journald = "systemd-journald"
|
||||||
|
|
||||||
-- | Enables persistent storage of the journal.
|
-- | Enables persistent storage of the journal.
|
||||||
persistentJournal :: Property NoInfo
|
persistentJournal :: Property NoInfo
|
||||||
persistentJournal = check (not <$> doesDirectoryExist dir) $
|
persistentJournal = check (not <$> doesDirectoryExist dir) $
|
||||||
|
@ -87,7 +134,8 @@ type Option = String
|
||||||
-- Does not ensure that the relevant daemon notices the change immediately.
|
-- Does not ensure that the relevant daemon notices the change immediately.
|
||||||
--
|
--
|
||||||
-- This assumes that there is only one [Header] per file, which is
|
-- This assumes that there is only one [Header] per file, which is
|
||||||
-- currently the case. And it assumes the file already exists with
|
-- currently the case for files like journald.conf and system.conf.
|
||||||
|
-- And it assumes the file already exists with
|
||||||
-- the right [Header], so new lines can just be appended to the end.
|
-- the right [Header], so new lines can just be appended to the end.
|
||||||
configured :: FilePath -> Option -> String -> Property NoInfo
|
configured :: FilePath -> Option -> String -> Property NoInfo
|
||||||
configured cfgfile option value = combineProperties desc
|
configured cfgfile option value = combineProperties desc
|
||||||
|
@ -102,15 +150,15 @@ configured cfgfile option value = combineProperties desc
|
||||||
| setting `isPrefixOf` l = Nothing
|
| setting `isPrefixOf` l = Nothing
|
||||||
| otherwise = Just l
|
| otherwise = Just l
|
||||||
|
|
||||||
|
-- | Causes systemd to reload its configuration files.
|
||||||
|
daemonReloaded :: Property NoInfo
|
||||||
|
daemonReloaded = trivial $ cmdProperty "systemctl" ["daemon-reload"]
|
||||||
|
|
||||||
-- | Configures journald, restarting it so the changes take effect.
|
-- | Configures journald, restarting it so the changes take effect.
|
||||||
journaldConfigured :: Option -> String -> Property NoInfo
|
journaldConfigured :: Option -> String -> Property NoInfo
|
||||||
journaldConfigured option value =
|
journaldConfigured option value =
|
||||||
configured "/etc/systemd/journald.conf" option value
|
configured "/etc/systemd/journald.conf" option value
|
||||||
`onChange` restarted "systemd-journald"
|
`onChange` restarted journald
|
||||||
|
|
||||||
-- | Causes systemd to reload its configuration files.
|
|
||||||
daemonReloaded :: Property NoInfo
|
|
||||||
daemonReloaded = trivial $ cmdProperty "systemctl" ["daemon-reload"]
|
|
||||||
|
|
||||||
-- | Defines a container with a given machine name.
|
-- | Defines a container with a given machine name.
|
||||||
--
|
--
|
||||||
|
@ -123,6 +171,7 @@ container :: MachineName -> (FilePath -> Chroot.Chroot) -> Container
|
||||||
container name mkchroot = Container name c h
|
container name mkchroot = Container name c h
|
||||||
& os system
|
& os system
|
||||||
& resolvConfed
|
& resolvConfed
|
||||||
|
& linkJournal
|
||||||
where
|
where
|
||||||
c@(Chroot.Chroot _ system _ _) = mkchroot (containerDir name)
|
c@(Chroot.Chroot _ system _ _) = mkchroot (containerDir name)
|
||||||
h = Host name [] mempty
|
h = Host name [] mempty
|
||||||
|
@ -153,8 +202,7 @@ nspawned c@(Container name (Chroot.Chroot loc system builderconf _) h) =
|
||||||
-- Chroot provisioning is run in systemd-only mode,
|
-- Chroot provisioning is run in systemd-only mode,
|
||||||
-- which sets up the chroot and ensures systemd and dbus are
|
-- which sets up the chroot and ensures systemd and dbus are
|
||||||
-- installed, but does not handle the other provisions.
|
-- installed, but does not handle the other provisions.
|
||||||
chrootprovisioned = Chroot.provisioned'
|
chrootprovisioned = Chroot.provisioned' (Chroot.propigateChrootInfo chroot) chroot True
|
||||||
(Chroot.propigateChrootInfo chroot) chroot True
|
|
||||||
|
|
||||||
-- Use nsenter to enter container and and run propellor to
|
-- Use nsenter to enter container and and run propellor to
|
||||||
-- finish provisioning.
|
-- finish provisioning.
|
||||||
|
@ -178,8 +226,14 @@ nspawnService (Container name _ _) cfg = setup <!> teardown
|
||||||
return $ unlines $
|
return $ unlines $
|
||||||
"# deployed by propellor" : map addparams ls
|
"# deployed by propellor" : map addparams ls
|
||||||
addparams l
|
addparams l
|
||||||
| "ExecStart=" `isPrefixOf` l =
|
| "ExecStart=" `isPrefixOf` l = unwords $
|
||||||
l ++ " " ++ unwords (nspawnServiceParams cfg)
|
[ "ExecStart = /usr/bin/systemd-nspawn"
|
||||||
|
, "--quiet"
|
||||||
|
, "--keep-unit"
|
||||||
|
, "--boot"
|
||||||
|
, "--directory=" ++ containerDir name
|
||||||
|
, "--machine=%i"
|
||||||
|
] ++ nspawnServiceParams cfg
|
||||||
| otherwise = l
|
| otherwise = l
|
||||||
|
|
||||||
goodservicefile = (==)
|
goodservicefile = (==)
|
||||||
|
@ -216,15 +270,19 @@ enterScript c@(Container name _ _) = setup <!> teardown
|
||||||
where
|
where
|
||||||
setup = combineProperties ("generated " ++ enterScriptFile c)
|
setup = combineProperties ("generated " ++ enterScriptFile c)
|
||||||
[ scriptfile `File.hasContent`
|
[ scriptfile `File.hasContent`
|
||||||
[ "#!/bin/sh"
|
[ "#!/usr/bin/perl"
|
||||||
, "# Generated by propellor"
|
, "# Generated by propellor"
|
||||||
, "pid=\"$(machinectl show " ++ shellEscape name ++ " -p Leader | cut -d= -f2)\" || true"
|
, "my $pid=`machinectl show " ++ shellEscape name ++ " -p Leader | cut -d= -f2`;"
|
||||||
, "if [ -n \"$pid\" ]; then"
|
, "chomp $pid;"
|
||||||
, "\tnsenter -p -u -n -i -m -t \"$pid\" \"$@\""
|
, "if (length $pid) {"
|
||||||
, "else"
|
, "\tforeach my $var (keys %ENV) {"
|
||||||
, "\techo container not running >&2"
|
, "\t\tdelete $ENV{$var} unless $var eq 'PATH' || $var eq 'TERM';"
|
||||||
, "\texit 1"
|
, "\t}"
|
||||||
, "fi"
|
, "\texec('nsenter', '-p', '-u', '-n', '-i', '-m', '-t', $pid, @ARGV);"
|
||||||
|
, "} else {"
|
||||||
|
, "\tdie 'container not running';"
|
||||||
|
, "}"
|
||||||
|
, "exit(1);"
|
||||||
]
|
]
|
||||||
, scriptfile `File.mode` combineModes (readModes ++ executeModes)
|
, scriptfile `File.mode` combineModes (readModes ++ executeModes)
|
||||||
]
|
]
|
||||||
|
@ -234,8 +292,8 @@ enterScript c@(Container name _ _) = setup <!> teardown
|
||||||
enterScriptFile :: Container -> FilePath
|
enterScriptFile :: Container -> FilePath
|
||||||
enterScriptFile (Container name _ _ ) = "/usr/local/bin/enter-" ++ mungename name
|
enterScriptFile (Container name _ _ ) = "/usr/local/bin/enter-" ++ mungename name
|
||||||
|
|
||||||
enterContainerProcess :: Container -> [String] -> CreateProcess
|
enterContainerProcess :: Container -> [String] -> IO (CreateProcess, IO ())
|
||||||
enterContainerProcess = proc . enterScriptFile
|
enterContainerProcess c ps = pure (proc (enterScriptFile c) ps, noop)
|
||||||
|
|
||||||
nspawnServiceName :: MachineName -> ServiceName
|
nspawnServiceName :: MachineName -> ServiceName
|
||||||
nspawnServiceName name = "systemd-nspawn@" ++ name ++ ".service"
|
nspawnServiceName name = "systemd-nspawn@" ++ name ++ ".service"
|
||||||
|
@ -267,3 +325,68 @@ containerCfg p = RevertableProperty (mk True) (mk False)
|
||||||
-- This property is enabled by default. Revert it to disable it.
|
-- This property is enabled by default. Revert it to disable it.
|
||||||
resolvConfed :: RevertableProperty
|
resolvConfed :: RevertableProperty
|
||||||
resolvConfed = containerCfg "bind=/etc/resolv.conf"
|
resolvConfed = containerCfg "bind=/etc/resolv.conf"
|
||||||
|
|
||||||
|
-- | Link the container's journal to the host's if possible.
|
||||||
|
-- (Only works if the host has persistent journal enabled.)
|
||||||
|
--
|
||||||
|
-- This property is enabled by default. Revert it to disable it.
|
||||||
|
linkJournal :: RevertableProperty
|
||||||
|
linkJournal = containerCfg "link-journal=try-guest"
|
||||||
|
|
||||||
|
-- | Disconnect networking of the container from the host.
|
||||||
|
privateNetwork :: RevertableProperty
|
||||||
|
privateNetwork = containerCfg "private-network"
|
||||||
|
|
||||||
|
class Publishable a where
|
||||||
|
toPublish :: a -> String
|
||||||
|
|
||||||
|
instance Publishable Port where
|
||||||
|
toPublish (Port n) = show n
|
||||||
|
|
||||||
|
instance Publishable (Bound Port) where
|
||||||
|
toPublish v = toPublish (hostSide v) ++ ":" ++ toPublish (containerSide v)
|
||||||
|
|
||||||
|
data Proto = TCP | UDP
|
||||||
|
|
||||||
|
instance Publishable (Proto, Bound Port) where
|
||||||
|
toPublish (TCP, fp) = "tcp:" ++ toPublish fp
|
||||||
|
toPublish (UDP, fp) = "udp:" ++ toPublish fp
|
||||||
|
|
||||||
|
-- | Publish a port from the container to the host.
|
||||||
|
--
|
||||||
|
-- This feature was first added in systemd version 220.
|
||||||
|
--
|
||||||
|
-- This property is only needed (and will only work) if the container
|
||||||
|
-- is configured to use private networking. Also, networkd should be enabled
|
||||||
|
-- both inside the container, and on the host. For example:
|
||||||
|
--
|
||||||
|
-- > foo :: Host
|
||||||
|
-- > foo = host "foo.example.com"
|
||||||
|
-- > & Systemd.running Systemd.networkd
|
||||||
|
-- > & Systemd.nspawned webserver
|
||||||
|
-- >
|
||||||
|
-- > webserver :: Systemd.container
|
||||||
|
-- > webserver = Systemd.container "webserver" (Chroot.debootstrapped (System (Debian Testing) "amd64") mempty)
|
||||||
|
-- > & Systemd.privateNetwork
|
||||||
|
-- > & Systemd.running Systemd.networkd
|
||||||
|
-- > & Systemd.publish (Port 80 ->- Port 8080)
|
||||||
|
-- > & Apt.installedRunning "apache2"
|
||||||
|
publish :: Publishable p => p -> RevertableProperty
|
||||||
|
publish p = containerCfg $ "--port=" ++ toPublish p
|
||||||
|
|
||||||
|
class Bindable a where
|
||||||
|
toBind :: a -> String
|
||||||
|
|
||||||
|
instance Bindable FilePath where
|
||||||
|
toBind f = f
|
||||||
|
|
||||||
|
instance Bindable (Bound FilePath) where
|
||||||
|
toBind v = hostSide v ++ ":" ++ containerSide v
|
||||||
|
|
||||||
|
-- | Bind mount a file or directory from the host into the container.
|
||||||
|
bind :: Bindable p => p -> RevertableProperty
|
||||||
|
bind p = containerCfg $ "--bind=" ++ toBind p
|
||||||
|
|
||||||
|
-- | Read-only mind mount.
|
||||||
|
bindRo :: Bindable p => p -> RevertableProperty
|
||||||
|
bindRo p = containerCfg $ "--bind-ro=" ++ toBind p
|
||||||
|
|
|
@ -103,13 +103,8 @@ bandwidthRate' s divby = case readSize dataUnits s of
|
||||||
Nothing -> property ("unable to parse " ++ s) noChange
|
Nothing -> property ("unable to parse " ++ s) noChange
|
||||||
|
|
||||||
hiddenServiceAvailable :: HiddenServiceName -> Int -> Property NoInfo
|
hiddenServiceAvailable :: HiddenServiceName -> Int -> Property NoInfo
|
||||||
hiddenServiceAvailable hn port = hiddenServiceHostName prop
|
hiddenServiceAvailable hn port = hiddenServiceHostName $ hiddenService hn port
|
||||||
where
|
where
|
||||||
prop = configured
|
|
||||||
[ ("HiddenServiceDir", varLib </> hn)
|
|
||||||
, ("HiddenServicePort", unwords [show port, "127.0.0.1:" ++ show port])
|
|
||||||
]
|
|
||||||
`describe` "hidden service available"
|
|
||||||
hiddenServiceHostName p = adjustPropertySatisfy p $ \satisfy -> do
|
hiddenServiceHostName p = adjustPropertySatisfy p $ \satisfy -> do
|
||||||
r <- satisfy
|
r <- satisfy
|
||||||
h <- liftIO $ readFile (varLib </> hn </> "hostname")
|
h <- liftIO $ readFile (varLib </> hn </> "hostname")
|
||||||
|
@ -164,7 +159,7 @@ type NickName = String
|
||||||
|
|
||||||
-- | Convert String to a valid tor NickName.
|
-- | Convert String to a valid tor NickName.
|
||||||
saneNickname :: String -> NickName
|
saneNickname :: String -> NickName
|
||||||
saneNickname s
|
saneNickname s
|
||||||
| null n = "unnamed"
|
| null n = "unnamed"
|
||||||
| otherwise = n
|
| otherwise = n
|
||||||
where
|
where
|
||||||
|
|
|
@ -8,7 +8,6 @@ module Propellor.Shim (setup, cleanEnv, file) where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.LinuxMkLibs
|
import Utility.LinuxMkLibs
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
import Utility.FileSystemEncoding
|
import Utility.FileSystemEncoding
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ import System.Posix.Files
|
||||||
-- Propellor may be running from an existing shim, in which case it's
|
-- Propellor may be running from an existing shim, in which case it's
|
||||||
-- simply reused.
|
-- simply reused.
|
||||||
setup :: FilePath -> Maybe FilePath -> FilePath -> IO FilePath
|
setup :: FilePath -> Maybe FilePath -> FilePath -> IO FilePath
|
||||||
setup propellorbin propellorbinpath dest = checkAlreadyShimmed propellorbin $ do
|
setup propellorbin propellorbinpath dest = checkAlreadyShimmed shim $ do
|
||||||
createDirectoryIfMissing True dest
|
createDirectoryIfMissing True dest
|
||||||
|
|
||||||
libs <- parseLdd <$> readProcess "ldd" [propellorbin]
|
libs <- parseLdd <$> readProcess "ldd" [propellorbin]
|
||||||
|
@ -40,7 +39,6 @@ setup propellorbin propellorbinpath dest = checkAlreadyShimmed propellorbin $ do
|
||||||
fromMaybe (error "cannot find gconv directory") $
|
fromMaybe (error "cannot find gconv directory") $
|
||||||
headMaybe $ filter ("/gconv/" `isInfixOf`) glibclibs
|
headMaybe $ filter ("/gconv/" `isInfixOf`) glibclibs
|
||||||
let linkerparams = ["--library-path", intercalate ":" libdirs ]
|
let linkerparams = ["--library-path", intercalate ":" libdirs ]
|
||||||
let shim = file propellorbin dest
|
|
||||||
writeFile shim $ unlines
|
writeFile shim $ unlines
|
||||||
[ shebang
|
[ shebang
|
||||||
, "GCONV_PATH=" ++ shellEscape gconvdir
|
, "GCONV_PATH=" ++ shellEscape gconvdir
|
||||||
|
@ -50,6 +48,8 @@ setup propellorbin propellorbinpath dest = checkAlreadyShimmed propellorbin $ do
|
||||||
]
|
]
|
||||||
modifyFileMode shim (addModes executeModes)
|
modifyFileMode shim (addModes executeModes)
|
||||||
return shim
|
return shim
|
||||||
|
where
|
||||||
|
shim = file propellorbin dest
|
||||||
|
|
||||||
shebang :: String
|
shebang :: String
|
||||||
shebang = "#!/bin/sh"
|
shebang = "#!/bin/sh"
|
||||||
|
|
|
@ -14,8 +14,7 @@ import System.Posix.Directory
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import qualified Network.BSD as BSD
|
import Network.Socket (getAddrInfo, defaultHints, AddrInfo(..), AddrInfoFlag(..), SockAddr)
|
||||||
import Network.Socket (inet_ntoa)
|
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Propellor.Protocol
|
import Propellor.Protocol
|
||||||
|
@ -98,17 +97,21 @@ spin target relay hst = do
|
||||||
getSshTarget :: HostName -> Host -> IO String
|
getSshTarget :: HostName -> Host -> IO String
|
||||||
getSshTarget target hst
|
getSshTarget target hst
|
||||||
| null configips = return target
|
| null configips = return target
|
||||||
| otherwise = go =<< tryIO (BSD.getHostByName target)
|
| otherwise = go =<< tryIO (dnslookup target)
|
||||||
where
|
where
|
||||||
go (Left e) = useip (show e)
|
go (Left e) = useip (show e)
|
||||||
go (Right hostentry) = ifM (anyM matchingconfig (BSD.hostAddresses hostentry))
|
go (Right addrinfos) = do
|
||||||
( return target
|
configaddrinfos <- catMaybes <$> mapM iptoaddr configips
|
||||||
, do
|
if any (`elem` configaddrinfos) (map addrAddress addrinfos)
|
||||||
ips <- mapM inet_ntoa (BSD.hostAddresses hostentry)
|
then return target
|
||||||
useip ("DNS " ++ show ips ++ " vs configured " ++ show configips)
|
else useip ("DNS lookup did not return any of the expected addresses " ++ show configips)
|
||||||
)
|
|
||||||
|
|
||||||
matchingconfig a = flip elem configips <$> inet_ntoa a
|
dnslookup h = getAddrInfo (Just $ defaultHints { addrFlags = [AI_CANONNAME] }) (Just h) Nothing
|
||||||
|
|
||||||
|
-- Convert a string containing an IP address into a SockAddr.
|
||||||
|
iptoaddr :: String -> IO (Maybe SockAddr)
|
||||||
|
iptoaddr ip = catchDefaultIO Nothing $ headMaybe . map addrAddress
|
||||||
|
<$> getAddrInfo (Just $ defaultHints { addrFlags = [AI_NUMERICHOST] }) (Just ip) Nothing
|
||||||
|
|
||||||
useip why = case headMaybe configips of
|
useip why = case headMaybe configips of
|
||||||
Nothing -> return target
|
Nothing -> return target
|
||||||
|
@ -144,11 +147,15 @@ update forhost = do
|
||||||
hout <- dup stdOutput
|
hout <- dup stdOutput
|
||||||
hClose stdin
|
hClose stdin
|
||||||
hClose stdout
|
hClose stdout
|
||||||
|
-- Not using git pull because git 2.5.0 badly
|
||||||
|
-- broke its option parser.
|
||||||
unlessM (boolSystem "git" (pullparams hin hout)) $
|
unlessM (boolSystem "git" (pullparams hin hout)) $
|
||||||
errorMessage "git pull from client failed"
|
errorMessage "git fetch from client failed"
|
||||||
|
unlessM (boolSystem "git" [Param "merge", Param "FETCH_HEAD"]) $
|
||||||
|
errorMessage "git merge from client failed"
|
||||||
where
|
where
|
||||||
pullparams hin hout =
|
pullparams hin hout =
|
||||||
[ Param "pull"
|
[ Param "fetch"
|
||||||
, Param "--progress"
|
, Param "--progress"
|
||||||
, Param "--upload-pack"
|
, Param "--upload-pack"
|
||||||
, Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
|
, Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module Propellor.Ssh where
|
module Propellor.Ssh where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.SafeCommand
|
|
||||||
import Utility.UserInfo
|
import Utility.UserInfo
|
||||||
|
|
||||||
import System.PosixCompat
|
import System.PosixCompat
|
||||||
|
@ -23,7 +22,8 @@ sshCachingParams hn = do
|
||||||
let ps =
|
let ps =
|
||||||
[ Param "-o"
|
[ Param "-o"
|
||||||
, Param ("ControlPath=" ++ socketfile)
|
, Param ("ControlPath=" ++ socketfile)
|
||||||
, Params "-o ControlMaster=auto -o ControlPersist=yes"
|
, Param "-o", Param "ControlMaster=auto"
|
||||||
|
, Param "-o", Param "ControlPersist=yes"
|
||||||
]
|
]
|
||||||
|
|
||||||
maybe noop (expireold ps socketfile)
|
maybe noop (expireold ps socketfile)
|
||||||
|
@ -38,7 +38,7 @@ sshCachingParams hn = do
|
||||||
then touchFile f
|
then touchFile f
|
||||||
else do
|
else do
|
||||||
void $ boolSystem "ssh" $
|
void $ boolSystem "ssh" $
|
||||||
[ Params "-O stop" ] ++ ps ++
|
[ Param "-O", Param "stop" ] ++ ps ++
|
||||||
[ Param "localhost" ]
|
[ Param "localhost" ]
|
||||||
nukeFile f
|
nukeFile f
|
||||||
tenminutes = 600
|
tenminutes = 600
|
||||||
|
|
|
@ -10,6 +10,7 @@ data CmdLine
|
||||||
| Spin [HostName] (Maybe HostName)
|
| Spin [HostName] (Maybe HostName)
|
||||||
| SimpleRun HostName
|
| SimpleRun HostName
|
||||||
| Set PrivDataField Context
|
| Set PrivDataField Context
|
||||||
|
| Unset PrivDataField Context
|
||||||
| Dump PrivDataField Context
|
| Dump PrivDataField Context
|
||||||
| Edit PrivDataField Context
|
| Edit PrivDataField Context
|
||||||
| ListFields
|
| ListFields
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
module Propellor.Types.Container where
|
||||||
|
|
||||||
|
-- | A value that can be bound between the host and a container.
|
||||||
|
--
|
||||||
|
-- For example, a Bound Port is a Port on the container that is bound to
|
||||||
|
-- a Port on the host.
|
||||||
|
data Bound v = Bound
|
||||||
|
{ hostSide :: v
|
||||||
|
, containerSide :: v
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Create a Bound value, from two different values for the host and
|
||||||
|
-- container.
|
||||||
|
--
|
||||||
|
-- For example, @Port 8080 -<- Port 80@ means that port 8080 on the host
|
||||||
|
-- is bound to port 80 from the container.
|
||||||
|
(-<-) :: (hostv ~ v, containerv ~ v) => hostv -> containerv -> Bound v
|
||||||
|
(-<-) hostv containerv = Bound hostv containerv
|
||||||
|
|
||||||
|
-- | Flipped version of -<- with the container value first and host value
|
||||||
|
-- second.
|
||||||
|
(->-) :: (containerv ~ v, hostv ~ v) => hostv -> containerv -> Bound v
|
||||||
|
(->-) containerv hostv = Bound hostv containerv
|
||||||
|
|
||||||
|
-- | Create a Bound value, that is the same on both the host and container.
|
||||||
|
same :: v -> Bound v
|
||||||
|
same v = Bound v v
|
||||||
|
|
|
@ -10,6 +10,7 @@ module Propellor.Types.OS (
|
||||||
User(..),
|
User(..),
|
||||||
Group(..),
|
Group(..),
|
||||||
userGroup,
|
userGroup,
|
||||||
|
Port(..),
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Network.BSD (HostName)
|
import Network.BSD (HostName)
|
||||||
|
@ -42,3 +43,6 @@ newtype Group = Group String
|
||||||
-- | Makes a Group with the same name as the User.
|
-- | Makes a Group with the same name as the User.
|
||||||
userGroup :: User -> Group
|
userGroup :: User -> Group
|
||||||
userGroup (User u) = Group u
|
userGroup (User u) = Group u
|
||||||
|
|
||||||
|
newtype Port = Port Int
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
- License: BSD-2-clause
|
- License: BSD-2-clause
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Data where
|
module Utility.Data where
|
||||||
|
|
||||||
{- First item in the list that is not Nothing. -}
|
{- First item in the list that is not Nothing. -}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Directory where
|
module Utility.Directory where
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ import Control.Applicative
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import System.IO.Unsafe (unsafeInterleaveIO)
|
import System.IO.Unsafe (unsafeInterleaveIO)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Prelude
|
||||||
|
|
||||||
#ifdef mingw32_HOST_OS
|
#ifdef mingw32_HOST_OS
|
||||||
import qualified System.Win32 as Win32
|
import qualified System.Win32 as Win32
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Env where
|
module Utility.Env where
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ module Utility.Env where
|
||||||
import Utility.Exception
|
import Utility.Exception
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Prelude
|
||||||
import qualified System.Environment as E
|
import qualified System.Environment as E
|
||||||
import qualified System.SetEnv
|
import qualified System.SetEnv
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Exception (
|
module Utility.Exception (
|
||||||
module X,
|
module X,
|
||||||
|
|
|
@ -22,15 +22,12 @@ import Utility.Exception
|
||||||
|
|
||||||
{- Applies a conversion function to a file's mode. -}
|
{- Applies a conversion function to a file's mode. -}
|
||||||
modifyFileMode :: FilePath -> (FileMode -> FileMode) -> IO ()
|
modifyFileMode :: FilePath -> (FileMode -> FileMode) -> IO ()
|
||||||
modifyFileMode f convert = void $ modifyFileMode' f convert
|
modifyFileMode f convert = do
|
||||||
modifyFileMode' :: FilePath -> (FileMode -> FileMode) -> IO FileMode
|
|
||||||
modifyFileMode' f convert = do
|
|
||||||
s <- getFileStatus f
|
s <- getFileStatus f
|
||||||
let old = fileMode s
|
let old = fileMode s
|
||||||
let new = convert old
|
let new = convert old
|
||||||
when (new /= old) $
|
when (new /= old) $
|
||||||
setFileMode f new
|
setFileMode f new
|
||||||
return old
|
|
||||||
|
|
||||||
{- Adds the specified FileModes to the input mode, leaving the rest
|
{- Adds the specified FileModes to the input mode, leaving the rest
|
||||||
- unchanged. -}
|
- unchanged. -}
|
||||||
|
@ -41,14 +38,6 @@ addModes ms m = combineModes (m:ms)
|
||||||
removeModes :: [FileMode] -> FileMode -> FileMode
|
removeModes :: [FileMode] -> FileMode -> FileMode
|
||||||
removeModes ms m = m `intersectFileModes` complement (combineModes ms)
|
removeModes ms m = m `intersectFileModes` complement (combineModes ms)
|
||||||
|
|
||||||
{- Runs an action after changing a file's mode, then restores the old mode. -}
|
|
||||||
withModifiedFileMode :: FilePath -> (FileMode -> FileMode) -> IO a -> IO a
|
|
||||||
withModifiedFileMode file convert a = bracket setup cleanup go
|
|
||||||
where
|
|
||||||
setup = modifyFileMode' file convert
|
|
||||||
cleanup oldmode = modifyFileMode file (const oldmode)
|
|
||||||
go _ = a
|
|
||||||
|
|
||||||
writeModes :: [FileMode]
|
writeModes :: [FileMode]
|
||||||
writeModes = [ownerWriteMode, groupWriteMode, otherWriteMode]
|
writeModes = [ownerWriteMode, groupWriteMode, otherWriteMode]
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.FileSystemEncoding (
|
module Utility.FileSystemEncoding (
|
||||||
fileEncoding,
|
fileEncoding,
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
|
|
||||||
module Utility.LinuxMkLibs where
|
module Utility.LinuxMkLibs where
|
||||||
|
|
||||||
import Control.Applicative
|
import Utility.PartialPrelude
|
||||||
|
import Utility.Directory
|
||||||
|
import Utility.Process
|
||||||
|
import Utility.Monad
|
||||||
|
import Utility.Path
|
||||||
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
|
@ -15,12 +20,8 @@ import Data.List.Utils
|
||||||
import System.Posix.Files
|
import System.Posix.Files
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Control.Monad.IfElse
|
import Control.Monad.IfElse
|
||||||
|
import Control.Applicative
|
||||||
import Utility.PartialPrelude
|
import Prelude
|
||||||
import Utility.Directory
|
|
||||||
import Utility.Process
|
|
||||||
import Utility.Monad
|
|
||||||
import Utility.Path
|
|
||||||
|
|
||||||
{- Installs a library. If the library is a symlink to another file,
|
{- Installs a library. If the library is a symlink to another file,
|
||||||
- install the file it links to, and update the symlink to be relative. -}
|
- install the file it links to, and update the symlink to be relative. -}
|
||||||
|
|
|
@ -6,23 +6,25 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Misc where
|
module Utility.Misc where
|
||||||
|
|
||||||
|
import Utility.FileSystemEncoding
|
||||||
|
import Utility.Monad
|
||||||
|
|
||||||
import System.IO
|
import System.IO
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Foreign
|
import Foreign
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.List
|
import Data.List
|
||||||
import Control.Applicative
|
|
||||||
import System.Exit
|
import System.Exit
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import System.Posix.Process (getAnyProcessStatus)
|
import System.Posix.Process (getAnyProcessStatus)
|
||||||
import Utility.Exception
|
import Utility.Exception
|
||||||
#endif
|
#endif
|
||||||
|
import Control.Applicative
|
||||||
import Utility.FileSystemEncoding
|
import Prelude
|
||||||
import Utility.Monad
|
|
||||||
|
|
||||||
{- A version of hgetContents that is not lazy. Ensures file is
|
{- A version of hgetContents that is not lazy. Ensures file is
|
||||||
- all read before it gets closed. -}
|
- all read before it gets closed. -}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
- License: BSD-2-clause
|
- License: BSD-2-clause
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Monad where
|
module Utility.Monad where
|
||||||
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
- them being accidentially used.
|
- them being accidentially used.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.PartialPrelude where
|
module Utility.PartialPrelude where
|
||||||
|
|
||||||
import qualified Data.Maybe
|
import qualified Data.Maybe
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE PackageImports, CPP #-}
|
{-# LANGUAGE PackageImports, CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Path where
|
module Utility.Path where
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
|
import Prelude
|
||||||
|
|
||||||
#ifdef mingw32_HOST_OS
|
#ifdef mingw32_HOST_OS
|
||||||
import qualified System.FilePath.Posix as Posix
|
import qualified System.FilePath.Posix as Posix
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.PosixFiles (
|
module Utility.PosixFiles (
|
||||||
module X,
|
module X,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
{- System.Process enhancements, including additional ways of running
|
{- System.Process enhancements, including additional ways of running
|
||||||
- processes, and logging.
|
- processes, and logging.
|
||||||
-
|
-
|
||||||
- Copyright 2012 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2015 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- License: BSD-2-clause
|
- License: BSD-2-clause
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP, Rank2Types #-}
|
{-# LANGUAGE CPP, Rank2Types #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Process (
|
module Utility.Process (
|
||||||
module X,
|
module X,
|
||||||
|
@ -54,6 +55,7 @@ import qualified System.Posix.IO
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
#endif
|
#endif
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Prelude
|
||||||
|
|
||||||
import Utility.Misc
|
import Utility.Misc
|
||||||
import Utility.Exception
|
import Utility.Exception
|
||||||
|
@ -63,8 +65,8 @@ type CreateProcessRunner = forall a. CreateProcess -> ((Maybe Handle, Maybe Hand
|
||||||
data StdHandle = StdinHandle | StdoutHandle | StderrHandle
|
data StdHandle = StdinHandle | StdoutHandle | StderrHandle
|
||||||
deriving (Eq)
|
deriving (Eq)
|
||||||
|
|
||||||
{- Normally, when reading from a process, it does not need to be fed any
|
-- | Normally, when reading from a process, it does not need to be fed any
|
||||||
- standard input. -}
|
-- standard input.
|
||||||
readProcess :: FilePath -> [String] -> IO String
|
readProcess :: FilePath -> [String] -> IO String
|
||||||
readProcess cmd args = readProcessEnv cmd args Nothing
|
readProcess cmd args = readProcessEnv cmd args Nothing
|
||||||
|
|
||||||
|
@ -82,9 +84,8 @@ readProcess' p = withHandle StdoutHandle createProcessSuccess p $ \h -> do
|
||||||
hClose h
|
hClose h
|
||||||
return output
|
return output
|
||||||
|
|
||||||
{- Runs an action to write to a process on its stdin,
|
-- | Runs an action to write to a process on its stdin,
|
||||||
- returns its output, and also allows specifying the environment.
|
-- returns its output, and also allows specifying the environment.
|
||||||
-}
|
|
||||||
writeReadProcessEnv
|
writeReadProcessEnv
|
||||||
:: FilePath
|
:: FilePath
|
||||||
-> [String]
|
-> [String]
|
||||||
|
@ -124,8 +125,8 @@ writeReadProcessEnv cmd args environ writestdin adjusthandle = do
|
||||||
, env = environ
|
, env = environ
|
||||||
}
|
}
|
||||||
|
|
||||||
{- Waits for a ProcessHandle, and throws an IOError if the process
|
-- | Waits for a ProcessHandle, and throws an IOError if the process
|
||||||
- did not exit successfully. -}
|
-- did not exit successfully.
|
||||||
forceSuccessProcess :: CreateProcess -> ProcessHandle -> IO ()
|
forceSuccessProcess :: CreateProcess -> ProcessHandle -> IO ()
|
||||||
forceSuccessProcess p pid = do
|
forceSuccessProcess p pid = do
|
||||||
code <- waitForProcess pid
|
code <- waitForProcess pid
|
||||||
|
@ -133,10 +134,10 @@ forceSuccessProcess p pid = do
|
||||||
ExitSuccess -> return ()
|
ExitSuccess -> return ()
|
||||||
ExitFailure n -> fail $ showCmd p ++ " exited " ++ show n
|
ExitFailure n -> fail $ showCmd p ++ " exited " ++ show n
|
||||||
|
|
||||||
{- Waits for a ProcessHandle and returns True if it exited successfully.
|
-- | Waits for a ProcessHandle and returns True if it exited successfully.
|
||||||
- Note that using this with createProcessChecked will throw away
|
-- Note that using this with createProcessChecked will throw away
|
||||||
- the Bool, and is only useful to ignore the exit code of a process,
|
-- the Bool, and is only useful to ignore the exit code of a process,
|
||||||
- while still waiting for it. -}
|
-- while still waiting for it. -}
|
||||||
checkSuccessProcess :: ProcessHandle -> IO Bool
|
checkSuccessProcess :: ProcessHandle -> IO Bool
|
||||||
checkSuccessProcess pid = do
|
checkSuccessProcess pid = do
|
||||||
code <- waitForProcess pid
|
code <- waitForProcess pid
|
||||||
|
@ -147,13 +148,13 @@ ignoreFailureProcess pid = do
|
||||||
void $ waitForProcess pid
|
void $ waitForProcess pid
|
||||||
return True
|
return True
|
||||||
|
|
||||||
{- Runs createProcess, then an action on its handles, and then
|
-- | Runs createProcess, then an action on its handles, and then
|
||||||
- forceSuccessProcess. -}
|
-- forceSuccessProcess.
|
||||||
createProcessSuccess :: CreateProcessRunner
|
createProcessSuccess :: CreateProcessRunner
|
||||||
createProcessSuccess p a = createProcessChecked (forceSuccessProcess p) p a
|
createProcessSuccess p a = createProcessChecked (forceSuccessProcess p) p a
|
||||||
|
|
||||||
{- Runs createProcess, then an action on its handles, and then
|
-- | Runs createProcess, then an action on its handles, and then
|
||||||
- a checker action on its exit code, which must wait for the process. -}
|
-- a checker action on its exit code, which must wait for the process.
|
||||||
createProcessChecked :: (ProcessHandle -> IO b) -> CreateProcessRunner
|
createProcessChecked :: (ProcessHandle -> IO b) -> CreateProcessRunner
|
||||||
createProcessChecked checker p a = do
|
createProcessChecked checker p a = do
|
||||||
t@(_, _, _, pid) <- createProcess p
|
t@(_, _, _, pid) <- createProcess p
|
||||||
|
@ -161,14 +162,14 @@ createProcessChecked checker p a = do
|
||||||
_ <- checker pid
|
_ <- checker pid
|
||||||
either E.throw return r
|
either E.throw return r
|
||||||
|
|
||||||
{- Leaves the process running, suitable for lazy streaming.
|
-- | Leaves the process running, suitable for lazy streaming.
|
||||||
- Note: Zombies will result, and must be waited on. -}
|
-- Note: Zombies will result, and must be waited on.
|
||||||
createBackgroundProcess :: CreateProcessRunner
|
createBackgroundProcess :: CreateProcessRunner
|
||||||
createBackgroundProcess p a = a =<< createProcess p
|
createBackgroundProcess p a = a =<< createProcess p
|
||||||
|
|
||||||
{- Runs a process, optionally feeding it some input, and
|
-- | Runs a process, optionally feeding it some input, and
|
||||||
- returns a transcript combining its stdout and stderr, and
|
-- returns a transcript combining its stdout and stderr, and
|
||||||
- whether it succeeded or failed. -}
|
-- whether it succeeded or failed.
|
||||||
processTranscript :: String -> [String] -> (Maybe String) -> IO (String, Bool)
|
processTranscript :: String -> [String] -> (Maybe String) -> IO (String, Bool)
|
||||||
processTranscript cmd opts input = processTranscript' cmd opts Nothing input
|
processTranscript cmd opts input = processTranscript' cmd opts Nothing input
|
||||||
|
|
||||||
|
@ -232,9 +233,9 @@ processTranscript' cmd opts environ input = do
|
||||||
hClose inh
|
hClose inh
|
||||||
writeinput Nothing _ = return ()
|
writeinput Nothing _ = return ()
|
||||||
|
|
||||||
{- Runs a CreateProcessRunner, on a CreateProcess structure, that
|
-- | Runs a CreateProcessRunner, on a CreateProcess structure, that
|
||||||
- is adjusted to pipe only from/to a single StdHandle, and passes
|
-- is adjusted to pipe only from/to a single StdHandle, and passes
|
||||||
- the resulting Handle to an action. -}
|
-- the resulting Handle to an action.
|
||||||
withHandle
|
withHandle
|
||||||
:: StdHandle
|
:: StdHandle
|
||||||
-> CreateProcessRunner
|
-> CreateProcessRunner
|
||||||
|
@ -256,7 +257,7 @@ withHandle h creator p a = creator p' $ a . select
|
||||||
| h == StderrHandle =
|
| h == StderrHandle =
|
||||||
(stderrHandle, base { std_err = CreatePipe })
|
(stderrHandle, base { std_err = CreatePipe })
|
||||||
|
|
||||||
{- Like withHandle, but passes (stdin, stdout) handles to the action. -}
|
-- | Like withHandle, but passes (stdin, stdout) handles to the action.
|
||||||
withIOHandles
|
withIOHandles
|
||||||
:: CreateProcessRunner
|
:: CreateProcessRunner
|
||||||
-> CreateProcess
|
-> CreateProcess
|
||||||
|
@ -270,7 +271,7 @@ withIOHandles creator p a = creator p' $ a . ioHandles
|
||||||
, std_err = Inherit
|
, std_err = Inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
{- Like withHandle, but passes (stdout, stderr) handles to the action. -}
|
-- | Like withHandle, but passes (stdout, stderr) handles to the action.
|
||||||
withOEHandles
|
withOEHandles
|
||||||
:: CreateProcessRunner
|
:: CreateProcessRunner
|
||||||
-> CreateProcess
|
-> CreateProcess
|
||||||
|
@ -284,8 +285,8 @@ withOEHandles creator p a = creator p' $ a . oeHandles
|
||||||
, std_err = CreatePipe
|
, std_err = CreatePipe
|
||||||
}
|
}
|
||||||
|
|
||||||
{- Forces the CreateProcessRunner to run quietly;
|
-- | Forces the CreateProcessRunner to run quietly;
|
||||||
- both stdout and stderr are discarded. -}
|
-- both stdout and stderr are discarded.
|
||||||
withQuietOutput
|
withQuietOutput
|
||||||
:: CreateProcessRunner
|
:: CreateProcessRunner
|
||||||
-> CreateProcess
|
-> CreateProcess
|
||||||
|
@ -297,8 +298,8 @@ withQuietOutput creator p = withFile devNull WriteMode $ \nullh -> do
|
||||||
}
|
}
|
||||||
creator p' $ const $ return ()
|
creator p' $ const $ return ()
|
||||||
|
|
||||||
{- Stdout and stderr are discarded, while the process is fed stdin
|
-- | Stdout and stderr are discarded, while the process is fed stdin
|
||||||
- from the handle. -}
|
-- from the handle.
|
||||||
feedWithQuietOutput
|
feedWithQuietOutput
|
||||||
:: CreateProcessRunner
|
:: CreateProcessRunner
|
||||||
-> CreateProcess
|
-> CreateProcess
|
||||||
|
@ -319,11 +320,11 @@ devNull = "/dev/null"
|
||||||
devNull = "NUL"
|
devNull = "NUL"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{- Extract a desired handle from createProcess's tuple.
|
-- | Extract a desired handle from createProcess's tuple.
|
||||||
- These partial functions are safe as long as createProcess is run
|
-- These partial functions are safe as long as createProcess is run
|
||||||
- with appropriate parameters to set up the desired handle.
|
-- with appropriate parameters to set up the desired handle.
|
||||||
- Get it wrong and the runtime crash will always happen, so should be
|
-- Get it wrong and the runtime crash will always happen, so should be
|
||||||
- easily noticed. -}
|
-- easily noticed.
|
||||||
type HandleExtractor = (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> Handle
|
type HandleExtractor = (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> Handle
|
||||||
stdinHandle :: HandleExtractor
|
stdinHandle :: HandleExtractor
|
||||||
stdinHandle (Just h, _, _, _) = h
|
stdinHandle (Just h, _, _, _) = h
|
||||||
|
@ -344,7 +345,7 @@ oeHandles _ = error "expected oeHandles"
|
||||||
processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle
|
processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle
|
||||||
processHandle (_, _, _, pid) = pid
|
processHandle (_, _, _, pid) = pid
|
||||||
|
|
||||||
{- Debugging trace for a CreateProcess. -}
|
-- | Debugging trace for a CreateProcess.
|
||||||
debugProcess :: CreateProcess -> IO ()
|
debugProcess :: CreateProcess -> IO ()
|
||||||
debugProcess p = do
|
debugProcess p = do
|
||||||
debugM "Utility.Process" $ unwords
|
debugM "Utility.Process" $ unwords
|
||||||
|
@ -360,15 +361,15 @@ debugProcess p = do
|
||||||
piped Inherit = False
|
piped Inherit = False
|
||||||
piped _ = True
|
piped _ = True
|
||||||
|
|
||||||
{- Shows the command that a CreateProcess will run. -}
|
-- | Shows the command that a CreateProcess will run.
|
||||||
showCmd :: CreateProcess -> String
|
showCmd :: CreateProcess -> String
|
||||||
showCmd = go . cmdspec
|
showCmd = go . cmdspec
|
||||||
where
|
where
|
||||||
go (ShellCommand s) = s
|
go (ShellCommand s) = s
|
||||||
go (RawCommand c ps) = c ++ " " ++ show ps
|
go (RawCommand c ps) = c ++ " " ++ show ps
|
||||||
|
|
||||||
{- Starts an interactive process. Unlike runInteractiveProcess in
|
-- | Starts an interactive process. Unlike runInteractiveProcess in
|
||||||
- System.Process, stderr is inherited. -}
|
-- System.Process, stderr is inherited.
|
||||||
startInteractiveProcess
|
startInteractiveProcess
|
||||||
:: FilePath
|
:: FilePath
|
||||||
-> [String]
|
-> [String]
|
||||||
|
@ -384,7 +385,8 @@ startInteractiveProcess cmd args environ = do
|
||||||
(Just from, Just to, _, pid) <- createProcess p
|
(Just from, Just to, _, pid) <- createProcess p
|
||||||
return (pid, to, from)
|
return (pid, to, from)
|
||||||
|
|
||||||
{- Wrapper around System.Process function that does debug logging. -}
|
-- | Wrapper around 'System.Process.createProcess' from System.Process,
|
||||||
|
-- that does debug logging.
|
||||||
createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
|
createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
|
||||||
createProcess p = do
|
createProcess p = do
|
||||||
debugProcess p
|
debugProcess p
|
||||||
|
|
|
@ -19,6 +19,7 @@ import System.Posix.Types
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
|
import Prelude
|
||||||
|
|
||||||
instance (Arbitrary k, Arbitrary v, Eq k, Ord k) => Arbitrary (M.Map k v) where
|
instance (Arbitrary k, Arbitrary v, Eq k, Ord k) => Arbitrary (M.Map k v) where
|
||||||
arbitrary = M.fromList <$> arbitrary
|
arbitrary = M.fromList <$> arbitrary
|
||||||
|
|
|
@ -5,44 +5,45 @@
|
||||||
- License: BSD-2-clause
|
- License: BSD-2-clause
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.SafeCommand where
|
module Utility.SafeCommand where
|
||||||
|
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import Utility.Process
|
import Utility.Process
|
||||||
import Data.String.Utils
|
import Data.String.Utils
|
||||||
import Control.Applicative
|
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
import Control.Applicative
|
||||||
|
import Prelude
|
||||||
|
|
||||||
{- A type for parameters passed to a shell command. A command can
|
-- | Parameters that can be passed to a shell command.
|
||||||
- be passed either some Params (multiple parameters can be included,
|
data CommandParam
|
||||||
- whitespace-separated, or a single Param (for when parameters contain
|
= Param String -- ^ A parameter
|
||||||
- whitespace), or a File.
|
| File FilePath -- ^ The name of a file
|
||||||
-}
|
|
||||||
data CommandParam = Params String | Param String | File FilePath
|
|
||||||
deriving (Eq, Show, Ord)
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
{- Used to pass a list of CommandParams to a function that runs
|
-- | Used to pass a list of CommandParams to a function that runs
|
||||||
- a command and expects Strings. -}
|
-- a command and expects Strings. -}
|
||||||
toCommand :: [CommandParam] -> [String]
|
toCommand :: [CommandParam] -> [String]
|
||||||
toCommand = concatMap unwrap
|
toCommand = map unwrap
|
||||||
where
|
where
|
||||||
unwrap (Param s) = [s]
|
unwrap (Param s) = s
|
||||||
unwrap (Params s) = filter (not . null) (split " " s)
|
|
||||||
-- Files that start with a non-alphanumeric that is not a path
|
-- Files that start with a non-alphanumeric that is not a path
|
||||||
-- separator are modified to avoid the command interpreting them as
|
-- separator are modified to avoid the command interpreting them as
|
||||||
-- options or other special constructs.
|
-- options or other special constructs.
|
||||||
unwrap (File s@(h:_))
|
unwrap (File s@(h:_))
|
||||||
| isAlphaNum h || h `elem` pathseps = [s]
|
| isAlphaNum h || h `elem` pathseps = s
|
||||||
| otherwise = ["./" ++ s]
|
| otherwise = "./" ++ s
|
||||||
unwrap (File s) = [s]
|
unwrap (File s) = s
|
||||||
-- '/' is explicitly included because it's an alternative
|
-- '/' is explicitly included because it's an alternative
|
||||||
-- path separator on Windows.
|
-- path separator on Windows.
|
||||||
pathseps = pathSeparator:"./"
|
pathseps = pathSeparator:"./"
|
||||||
|
|
||||||
{- Run a system command, and returns True or False
|
-- | Run a system command, and returns True or False if it succeeded or failed.
|
||||||
- if it succeeded or failed.
|
--
|
||||||
-}
|
-- This and other command running functions in this module log the commands
|
||||||
|
-- run at debug level, using System.Log.Logger.
|
||||||
boolSystem :: FilePath -> [CommandParam] -> IO Bool
|
boolSystem :: FilePath -> [CommandParam] -> IO Bool
|
||||||
boolSystem command params = boolSystem' command params id
|
boolSystem command params = boolSystem' command params id
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ boolSystemEnv :: FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bo
|
||||||
boolSystemEnv command params environ = boolSystem' command params $
|
boolSystemEnv command params environ = boolSystem' command params $
|
||||||
\p -> p { env = environ }
|
\p -> p { env = environ }
|
||||||
|
|
||||||
{- Runs a system command, returning the exit status. -}
|
-- | Runs a system command, returning the exit status.
|
||||||
safeSystem :: FilePath -> [CommandParam] -> IO ExitCode
|
safeSystem :: FilePath -> [CommandParam] -> IO ExitCode
|
||||||
safeSystem command params = safeSystem' command params id
|
safeSystem command params = safeSystem' command params id
|
||||||
|
|
||||||
|
@ -71,23 +72,22 @@ safeSystemEnv :: FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Ex
|
||||||
safeSystemEnv command params environ = safeSystem' command params $
|
safeSystemEnv command params environ = safeSystem' command params $
|
||||||
\p -> p { env = environ }
|
\p -> p { env = environ }
|
||||||
|
|
||||||
{- Wraps a shell command line inside sh -c, allowing it to be run in a
|
-- | Wraps a shell command line inside sh -c, allowing it to be run in a
|
||||||
- login shell that may not support POSIX shell, eg csh. -}
|
-- login shell that may not support POSIX shell, eg csh.
|
||||||
shellWrap :: String -> String
|
shellWrap :: String -> String
|
||||||
shellWrap cmdline = "sh -c " ++ shellEscape cmdline
|
shellWrap cmdline = "sh -c " ++ shellEscape cmdline
|
||||||
|
|
||||||
{- Escapes a filename or other parameter to be safely able to be exposed to
|
-- | Escapes a filename or other parameter to be safely able to be exposed to
|
||||||
- the shell.
|
-- the shell.
|
||||||
-
|
--
|
||||||
- This method works for POSIX shells, as well as other shells like csh.
|
-- This method works for POSIX shells, as well as other shells like csh.
|
||||||
-}
|
|
||||||
shellEscape :: String -> String
|
shellEscape :: String -> String
|
||||||
shellEscape f = "'" ++ escaped ++ "'"
|
shellEscape f = "'" ++ escaped ++ "'"
|
||||||
where
|
where
|
||||||
-- replace ' with '"'"'
|
-- replace ' with '"'"'
|
||||||
escaped = join "'\"'\"'" $ split "'" f
|
escaped = join "'\"'\"'" $ split "'" f
|
||||||
|
|
||||||
{- Unescapes a set of shellEscaped words or filenames. -}
|
-- | Unescapes a set of shellEscaped words or filenames.
|
||||||
shellUnEscape :: String -> [String]
|
shellUnEscape :: String -> [String]
|
||||||
shellUnEscape [] = []
|
shellUnEscape [] = []
|
||||||
shellUnEscape s = word : shellUnEscape rest
|
shellUnEscape s = word : shellUnEscape rest
|
||||||
|
@ -104,19 +104,19 @@ shellUnEscape s = word : shellUnEscape rest
|
||||||
| c == q = findword w cs
|
| c == q = findword w cs
|
||||||
| otherwise = inquote q (w++[c]) cs
|
| otherwise = inquote q (w++[c]) cs
|
||||||
|
|
||||||
{- For quickcheck. -}
|
-- | For quickcheck.
|
||||||
prop_idempotent_shellEscape :: String -> Bool
|
prop_idempotent_shellEscape :: String -> Bool
|
||||||
prop_idempotent_shellEscape s = [s] == (shellUnEscape . shellEscape) s
|
prop_idempotent_shellEscape s = [s] == (shellUnEscape . shellEscape) s
|
||||||
prop_idempotent_shellEscape_multiword :: [String] -> Bool
|
prop_idempotent_shellEscape_multiword :: [String] -> Bool
|
||||||
prop_idempotent_shellEscape_multiword s = s == (shellUnEscape . unwords . map shellEscape) s
|
prop_idempotent_shellEscape_multiword s = s == (shellUnEscape . unwords . map shellEscape) s
|
||||||
|
|
||||||
{- Segments a list of filenames into groups that are all below the maximum
|
-- | Segments a list of filenames into groups that are all below the maximum
|
||||||
- command-line length limit. -}
|
-- command-line length limit.
|
||||||
segmentXargsOrdered :: [FilePath] -> [[FilePath]]
|
segmentXargsOrdered :: [FilePath] -> [[FilePath]]
|
||||||
segmentXargsOrdered = reverse . map reverse . segmentXargsUnordered
|
segmentXargsOrdered = reverse . map reverse . segmentXargsUnordered
|
||||||
|
|
||||||
{- Not preserving data is a little faster, and streams better when
|
-- | Not preserving order is a little faster, and streams better when
|
||||||
- there are a great many filesnames. -}
|
-- there are a great many filenames.
|
||||||
segmentXargsUnordered :: [FilePath] -> [[FilePath]]
|
segmentXargsUnordered :: [FilePath] -> [[FilePath]]
|
||||||
segmentXargsUnordered l = go l [] 0 []
|
segmentXargsUnordered l = go l [] 0 []
|
||||||
where
|
where
|
||||||
|
|
|
@ -32,7 +32,6 @@ import Utility.QuickCheck
|
||||||
import Utility.PartialPrelude
|
import Utility.PartialPrelude
|
||||||
import Utility.Misc
|
import Utility.Misc
|
||||||
|
|
||||||
import Control.Applicative
|
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Time.LocalTime
|
import Data.Time.LocalTime
|
||||||
|
@ -41,6 +40,8 @@ import Data.Time.Calendar.WeekDate
|
||||||
import Data.Time.Calendar.OrdinalDate
|
import Data.Time.Calendar.OrdinalDate
|
||||||
import Data.Tuple.Utils
|
import Data.Tuple.Utils
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
import Control.Applicative
|
||||||
|
import Prelude
|
||||||
|
|
||||||
{- Some sort of scheduled event. -}
|
{- Some sort of scheduled event. -}
|
||||||
data Schedule = Schedule Recurrance ScheduledTime
|
data Schedule = Schedule Recurrance ScheduledTime
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.Tmp where
|
module Utility.Tmp where
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
module Utility.UserInfo (
|
module Utility.UserInfo (
|
||||||
myHomeDir,
|
myHomeDir,
|
||||||
|
@ -13,12 +14,13 @@ module Utility.UserInfo (
|
||||||
myUserGecos,
|
myUserGecos,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Utility.Env
|
||||||
|
|
||||||
import System.PosixCompat
|
import System.PosixCompat
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
#endif
|
#endif
|
||||||
|
import Prelude
|
||||||
import Utility.Env
|
|
||||||
|
|
||||||
{- Current user's home directory.
|
{- Current user's home directory.
|
||||||
-
|
-
|
||||||
|
|
Loading…
Reference in New Issue