From 69d21fe15d60722cdb3e413b8c089da1cb5b6a9e Mon Sep 17 00:00:00 2001 From: Lukas Rusak Date: Mon, 4 Apr 2016 14:20:04 -0700 Subject: [PATCH 1/2] go: initial package --- packages/addons/addon-depends/go/package.mk | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 packages/addons/addon-depends/go/package.mk diff --git a/packages/addons/addon-depends/go/package.mk b/packages/addons/addon-depends/go/package.mk new file mode 100644 index 0000000000..742a38cc05 --- /dev/null +++ b/packages/addons/addon-depends/go/package.mk @@ -0,0 +1,60 @@ +################################################################################ +# This file is part of LibreELEC - http://www.libreelec.tv +# Copyright (C) 2009-2016 Lukas Rusak (lrusak@libreelec.tv) +# +# LibreELEC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# LibreELEC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LibreELEC. If not, see . +################################################################################ + +PKG_NAME="go" +PKG_VERSION="1.6" +PKG_REV="1" +PKG_ARCH="any" +PKG_LICENSE="BSD" +PKG_SITE="https://golang.org" +PKG_URL="https://github.com/golang/go/archive/${PKG_NAME}${PKG_VERSION}.tar.gz" +PKG_SOURCE_DIR="${PKG_NAME}-${PKG_NAME}${PKG_VERSION}" +PKG_DEPENDS_HOST="toolchain" +PKG_PRIORITY="optional" +PKG_SECTION="system" +PKG_SHORTDESC="Go is an open source programming language that makes it easy to build simple, reliable, and efficient software." +PKG_LONGDESC="Go is an open source programming language that makes it easy to build simple, reliable, and efficient software." + +PKG_IS_ADDON="no" +PKG_AUTORECONF="no" + +#################################################################### +# On Fedora `dnf install golang` will install go to /usr/lib/golang +# +# On Ubuntu you need to install golang manually, similar to: +# $ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz +# $ tar xf go1.6.linux-amd64.tar.gz -C /opt/ +# $ ln -s /opt/go /usr/lib/golang +#################################################################### + +configure_host() { + export GOOS=linux + export GOROOT_FINAL=$ROOT/$TOOLCHAIN/lib/golang + export GOROOT_BOOTSTRAP=/usr/lib/golang + export GOARCH=amd64 +} + +make_host() { + cd $ROOT/$PKG_BUILD/src + bash make.bash --no-banner +} + +makeinstall_host() { + mkdir -p $ROOT/$TOOLCHAIN/lib/golang + cp -av $ROOT/$PKG_BUILD/* $ROOT/$TOOLCHAIN/lib/golang/ +} From e88d8e811a2b6a5cfbd8eda1d0b93d1ac6581f1e Mon Sep 17 00:00:00 2001 From: Lukas Rusak Date: Mon, 4 Apr 2016 14:20:54 -0700 Subject: [PATCH 2/2] docker: initial add-on --- packages/addons/service/docker/changelog.txt | 2 + packages/addons/service/docker/icon/icon.png | Bin 0 -> 29284 bytes packages/addons/service/docker/package.mk | 96 ++++++ ...ocker-001-use-addon-storage-location.patch | 290 ++++++++++++++++++ .../service/docker/source/bin/docker-config | 37 +++ .../service/docker/source/config/docker.conf | 2 + .../addons/service/docker/source/default.py | 103 +++++++ .../source/examples/couchpotato.service | 22 ++ .../docker/source/examples/mysql.service | 26 ++ .../docker/source/examples/sabnzbd.service | 22 ++ .../docker/source/examples/sickbeard.service | 22 ++ .../source/examples/transmission.service | 24 ++ .../source/examples/var-lib-docker.mount | 14 + .../systemd/service.system.docker.service | 19 ++ .../systemd/service.system.docker.socket | 13 + 15 files changed, 692 insertions(+) create mode 100644 packages/addons/service/docker/changelog.txt create mode 100644 packages/addons/service/docker/icon/icon.png create mode 100644 packages/addons/service/docker/package.mk create mode 100644 packages/addons/service/docker/patches/docker-001-use-addon-storage-location.patch create mode 100755 packages/addons/service/docker/source/bin/docker-config create mode 100644 packages/addons/service/docker/source/config/docker.conf create mode 100644 packages/addons/service/docker/source/default.py create mode 100644 packages/addons/service/docker/source/examples/couchpotato.service create mode 100644 packages/addons/service/docker/source/examples/mysql.service create mode 100644 packages/addons/service/docker/source/examples/sabnzbd.service create mode 100644 packages/addons/service/docker/source/examples/sickbeard.service create mode 100644 packages/addons/service/docker/source/examples/transmission.service create mode 100644 packages/addons/service/docker/source/examples/var-lib-docker.mount create mode 100644 packages/addons/service/docker/source/systemd/service.system.docker.service create mode 100644 packages/addons/service/docker/source/systemd/service.system.docker.socket diff --git a/packages/addons/service/docker/changelog.txt b/packages/addons/service/docker/changelog.txt new file mode 100644 index 0000000000..f9dfb73a63 --- /dev/null +++ b/packages/addons/service/docker/changelog.txt @@ -0,0 +1,2 @@ +7.0.100 +- Initial release diff --git a/packages/addons/service/docker/icon/icon.png b/packages/addons/service/docker/icon/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c9f07eb63bb3347020e9140bc6594ae2c17cb75a GIT binary patch literal 29284 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 literal 0 HcmV?d00001 diff --git a/packages/addons/service/docker/package.mk b/packages/addons/service/docker/package.mk new file mode 100644 index 0000000000..6ed609d86a --- /dev/null +++ b/packages/addons/service/docker/package.mk @@ -0,0 +1,96 @@ +################################################################################ +# This file is part of LibreELEC - http://www.libreelec.tv +# Copyright (C) 2009-2016 Lukas Rusak (lrusak@libreelec.tv) +# +# LibreELEC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# LibreELEC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LibreELEC. If not, see . +################################################################################ + +PKG_NAME="docker" +PKG_VERSION="1.10.3" +PKG_REV="100" +PKG_ARCH="any" +PKG_ADDON_PROJECTS="Generic RPi RPi2" +PKG_LICENSE="ASL" +PKG_SITE="http://www.docker.com/" +PKG_URL="https://github.com/docker/docker/archive/v${PKG_VERSION}.tar.gz" +PKG_DEPENDS_TARGET="toolchain sqlite go:host" +PKG_PRIORITY="optional" +PKG_SECTION="service/system" +PKG_SHORTDESC="Docker is an open-source engine that automates the deployment of any application as a lightweight, portable, self-sufficient container that will run virtually anywhere." +PKG_LONGDESC="Docker containers can encapsulate any payload, and will run consistently on and between virtually any server. The same container that a developer builds and tests on a laptop will run at scale, in production*, on VMs, bare-metal servers, OpenStack clusters, public instances, or combinations of the above." +PKG_AUTORECONF="no" + +PKG_IS_ADDON="yes" +PKG_ADDON_NAME="Docker" +PKG_ADDON_TYPE="xbmc.service" +PKG_ADDON_REPOVERSION="7.0" + +configure_target() { + export DOCKER_BUILDTAGS="daemon \ + exclude_graphdriver_devicemapper \ + exclude_graphdriver_aufs \ + exclude_graphdriver_btrfs" + + case $TARGET_ARCH in + x86_64) + export GOARCH=amd64 + ;; + arm) + export GOARCH=arm + + case $TARGET_CPU in + arm1176jzf-s) + export GOARM=6 + ;; + cortex-a7) + export GOARM=7 + ;; + esac + ;; + esac + + export GOOS=linux + export CGO_ENABLED=1 + export CGO_NO_EMULATION=1 + export CGO_CFLAGS=$CFLAGS + export LDFLAGS="-w -linkmode external -extldflags -Wl,--unresolved-symbols=ignore-in-shared-libs -extld $TARGET_CC" + export GOLANG=$ROOT/$TOOLCHAIN/lib/golang/bin/go + export GOPATH=$ROOT/$PKG_BUILD/.gopath:$ROOT/$PKG_BUILD/vendor + export GOROOT=$ROOT/$TOOLCHAIN/lib/golang + export PATH=$PATH:$GOROOT/bin + + ./hack/vendor.sh + + ln -fs $ROOT/$PKG_BUILD $ROOT/$PKG_BUILD/vendor/src/github.com/docker/docker + + # used for docker version + export GITCOMMIT=$PKG_VERSION + export VERSION=$PKG_VERSION + export BUILDTIME="$(date --utc)" + bash ./hack/make/.go-autogen +} + +make_target() { + mkdir -p bin + $GOLANG build -v -o bin/docker -a -tags "$DOCKER_BUILDTAGS" -ldflags "$LDFLAGS" ./docker +} + +makeinstall_target() { + : +} + +addon() { + mkdir -p $ADDON_BUILD/$PKG_ADDON_ID/bin/ + cp -a $ROOT/$PKG_BUILD/bin/docker $ADDON_BUILD/$PKG_ADDON_ID/bin/ +} diff --git a/packages/addons/service/docker/patches/docker-001-use-addon-storage-location.patch b/packages/addons/service/docker/patches/docker-001-use-addon-storage-location.patch new file mode 100644 index 0000000000..cd97127c2d --- /dev/null +++ b/packages/addons/service/docker/patches/docker-001-use-addon-storage-location.patch @@ -0,0 +1,290 @@ +# Created with +# find . -name "*.go" -print | xargs sed -i 's/\/etc\/docker/\/storage\/.kodi\/userdata\/addon_data\/service.system.docker\/config/g' + +diff -Naur a/docker/daemon_unix.go b/docker/daemon_unix.go +--- a/docker/daemon_unix.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/docker/daemon_unix.go 2016-02-19 00:28:50.662085695 +0100 +@@ -16,7 +16,7 @@ + _ "github.com/docker/docker/daemon/execdriver/native" + ) + +-const defaultDaemonConfigFile = "/etc/docker/daemon.json" ++const defaultDaemonConfigFile = "/storage/.kodi/userdata/addon_data/service.system.docker/config/daemon.json" + + func setPlatformServerConfig(serverConfig *apiserver.Config, daemonCfg *daemon.Config) *apiserver.Config { + serverConfig.SocketGroup = daemonCfg.SocketGroup +@@ -50,7 +50,7 @@ + } + + func getDaemonConfDir() string { +- return "/etc/docker" ++ return "/storage/.kodi/userdata/addon_data/service.system.docker/config" + } + + // setupConfigReloadTrap configures the USR2 signal to reload the configuration. +diff -Naur a/integration-cli/docker_cli_authz_unix_test.go b/integration-cli/docker_cli_authz_unix_test.go +--- a/integration-cli/docker_cli_authz_unix_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_authz_unix_test.go 2016-02-19 00:28:50.642085661 +0100 +@@ -121,10 +121,10 @@ + } + }) + +- err := os.MkdirAll("/etc/docker/plugins", 0755) ++ err := os.MkdirAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", 0755) + c.Assert(err, checker.IsNil) + +- fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin) ++ fileName := fmt.Sprintf("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/%s.spec", testAuthZPlugin) + err = ioutil.WriteFile(fileName, []byte(s.server.URL), 0644) + c.Assert(err, checker.IsNil) + } +@@ -163,7 +163,7 @@ + + s.server.Close() + +- err := os.RemoveAll("/etc/docker/plugins") ++ err := os.RemoveAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins") + c.Assert(err, checker.IsNil) + } + +diff -Naur a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go +--- a/integration-cli/docker_cli_daemon_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_daemon_test.go 2016-02-19 00:28:50.636085651 +0100 +@@ -537,13 +537,13 @@ + + func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) { + // TODO: skip or update for Windows daemon +- os.Remove("/etc/docker/key.json") ++ os.Remove("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + if err := s.d.Start(); err != nil { + c.Fatalf("Could not start daemon: %v", err) + } + s.d.Stop() + +- k, err := libtrust.LoadKeyFile("/etc/docker/key.json") ++ k, err := libtrust.LoadKeyFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + if err != nil { + c.Fatalf("Error opening key file") + } +@@ -556,7 +556,7 @@ + + func (s *DockerDaemonSuite) TestDaemonKeyMigration(c *check.C) { + // TODO: skip or update for Windows daemon +- os.Remove("/etc/docker/key.json") ++ os.Remove("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + k1, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + c.Fatalf("Error generating private key: %s", err) +@@ -573,7 +573,7 @@ + } + s.d.Stop() + +- k2, err := libtrust.LoadKeyFile("/etc/docker/key.json") ++ k2, err := libtrust.LoadKeyFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + if err != nil { + c.Fatalf("Error opening key file") + } +@@ -1337,7 +1337,7 @@ + Y string `json:"y"` + } + +- os.Remove("/etc/docker/key.json") ++ os.Remove("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + if err := s.d.Start(); err != nil { + c.Fatalf("Failed to start daemon: %v", err) + } +@@ -1347,7 +1347,7 @@ + } + + config := &Config{} +- bytes, err := ioutil.ReadFile("/etc/docker/key.json") ++ bytes, err := ioutil.ReadFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + if err != nil { + c.Fatalf("Error reading key.json file: %s", err) + } +@@ -1367,11 +1367,11 @@ + } + + // write back +- if err := ioutil.WriteFile("/etc/docker/key.json", newBytes, 0400); err != nil { ++ if err := ioutil.WriteFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json", newBytes, 0400); err != nil { + c.Fatalf("Error ioutil.WriteFile: %s", err) + } + +- defer os.Remove("/etc/docker/key.json") ++ defer os.Remove("/storage/.kodi/userdata/addon_data/service.system.docker/config/key.json") + + if err := s.d.Start(); err == nil { + c.Fatalf("It should not be successful to start daemon with wrong key: %v", err) +diff -Naur a/integration-cli/docker_cli_external_graphdriver_unix_test.go b/integration-cli/docker_cli_external_graphdriver_unix_test.go +--- a/integration-cli/docker_cli_external_graphdriver_unix_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_external_graphdriver_unix_test.go 2016-02-19 00:28:50.632085644 +0100 +@@ -283,18 +283,18 @@ + respond(w, &graphDriverResponse{Size: size}) + }) + +- err = os.MkdirAll("/etc/docker/plugins", 0755) +- c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins")) ++ err = os.MkdirAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", 0755) ++ c.Assert(err, check.IsNil, check.Commentf("error creating /storage/.kodi/userdata/addon_data/service.system.docker/config/plugins")) + +- err = ioutil.WriteFile("/etc/docker/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644) +- c.Assert(err, check.IsNil, check.Commentf("error writing to /etc/docker/plugins/test-external-graph-driver.spec")) ++ err = ioutil.WriteFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644) ++ c.Assert(err, check.IsNil, check.Commentf("error writing to /storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/test-external-graph-driver.spec")) + } + + func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) { + s.server.Close() + +- err := os.RemoveAll("/etc/docker/plugins") +- c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins")) ++ err := os.RemoveAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins") ++ c.Assert(err, check.IsNil, check.Commentf("error removing /storage/.kodi/userdata/addon_data/service.system.docker/config/plugins")) + } + + func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) { +diff -Naur a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go +--- a/integration-cli/docker_cli_network_unix_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_network_unix_test.go 2016-02-19 00:28:50.629085639 +0100 +@@ -201,14 +201,14 @@ + } + }) + +- err := os.MkdirAll("/etc/docker/plugins", 0755) ++ err := os.MkdirAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", 0755) + c.Assert(err, checker.IsNil) + +- fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv) ++ fileName := fmt.Sprintf("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/%s.spec", netDrv) + err = ioutil.WriteFile(fileName, []byte(url), 0644) + c.Assert(err, checker.IsNil) + +- ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv) ++ ipamFileName := fmt.Sprintf("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/%s.spec", ipamDrv) + err = ioutil.WriteFile(ipamFileName, []byte(url), 0644) + c.Assert(err, checker.IsNil) + } +@@ -220,7 +220,7 @@ + + s.server.Close() + +- err := os.RemoveAll("/etc/docker/plugins") ++ err := os.RemoveAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins") + c.Assert(err, checker.IsNil) + } + +diff -Naur a/integration-cli/docker_cli_start_volume_driver_unix_test.go b/integration-cli/docker_cli_start_volume_driver_unix_test.go +--- a/integration-cli/docker_cli_start_volume_driver_unix_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_start_volume_driver_unix_test.go 2016-02-19 00:28:50.651085676 +0100 +@@ -206,17 +206,17 @@ + send(w, nil) + }) + +- err := os.MkdirAll("/etc/docker/plugins", 0755) ++ err := os.MkdirAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", 0755) + c.Assert(err, checker.IsNil) + +- err = ioutil.WriteFile("/etc/docker/plugins/test-external-volume-driver.spec", []byte(s.server.URL), 0644) ++ err = ioutil.WriteFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/test-external-volume-driver.spec", []byte(s.server.URL), 0644) + c.Assert(err, checker.IsNil) + } + + func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) { + s.server.Close() + +- err := os.RemoveAll("/etc/docker/plugins") ++ err := os.RemoveAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins") + c.Assert(err, checker.IsNil) + } + +@@ -301,7 +301,7 @@ + + // Make sure a request to use a down driver doesn't block other requests + func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverLookupNotBlocked(c *check.C) { +- specPath := "/etc/docker/plugins/down-driver.spec" ++ specPath := "/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/down-driver.spec" + err := ioutil.WriteFile(specPath, []byte("tcp://127.0.0.7:9999"), 0644) + c.Assert(err, check.IsNil) + defer os.RemoveAll(specPath) +@@ -340,7 +340,7 @@ + err := s.d.StartWithBusybox() + c.Assert(err, checker.IsNil) + +- specPath := "/etc/docker/plugins/test-external-volume-driver-retry.spec" ++ specPath := "/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/test-external-volume-driver-retry.spec" + os.RemoveAll(specPath) + defer os.RemoveAll(specPath) + +diff -Naur a/integration-cli/docker_cli_volume_driver_compat_unix_test.go b/integration-cli/docker_cli_volume_driver_compat_unix_test.go +--- a/integration-cli/docker_cli_volume_driver_compat_unix_test.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/integration-cli/docker_cli_volume_driver_compat_unix_test.go 2016-02-19 00:28:50.646085667 +0100 +@@ -173,17 +173,17 @@ + send(w, nil) + }) + +- err := os.MkdirAll("/etc/docker/plugins", 0755) ++ err := os.MkdirAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", 0755) + c.Assert(err, checker.IsNil) + +- err = ioutil.WriteFile("/etc/docker/plugins/test-external-volume-driver.spec", []byte(s.server.URL), 0644) ++ err = ioutil.WriteFile("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins/test-external-volume-driver.spec", []byte(s.server.URL), 0644) + c.Assert(err, checker.IsNil) + } + + func (s *DockerExternalVolumeSuiteCompatV1_1) TearDownSuite(c *check.C) { + s.server.Close() + +- err := os.RemoveAll("/etc/docker/plugins") ++ err := os.RemoveAll("/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins") + c.Assert(err, checker.IsNil) + } + +diff -Naur a/pkg/plugins/discovery.go b/pkg/plugins/discovery.go +--- a/pkg/plugins/discovery.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/pkg/plugins/discovery.go 2016-02-19 00:28:50.247084996 +0100 +@@ -15,7 +15,7 @@ + // ErrNotFound plugin not found + ErrNotFound = errors.New("plugin not found") + socketsPath = "/run/docker/plugins" +- specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"} ++ specsPaths = []string{"/storage/.kodi/userdata/addon_data/service.system.docker/config/plugins", "/usr/lib/docker/plugins"} + ) + + // localRegistry defines a registry that is local (using unix socket). +diff -Naur a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go +--- a/pkg/plugins/plugins.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/pkg/plugins/plugins.go 2016-02-19 00:28:50.263085023 +0100 +@@ -4,7 +4,7 @@ + // Docker discovers plugins by looking for them in the plugin directory whenever + // a user or container tries to use one by name. UNIX domain socket files must + // be located under /run/docker/plugins, whereas spec files can be located +-// either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled ++// either under /storage/.kodi/userdata/addon_data/service.system.docker/config/plugins or /usr/lib/docker/plugins. This is handled + // by the Registry interface, which lets you list all plugins or get a plugin by + // its name if it exists. + // +diff -Naur a/registry/config_unix.go b/registry/config_unix.go +--- a/registry/config_unix.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/registry/config_unix.go 2016-02-19 00:28:50.742085829 +0100 +@@ -12,7 +12,7 @@ + + var ( + // CertsDir is the directory where certificates are stored +- CertsDir = "/etc/docker/certs.d" ++ CertsDir = "/storage/.kodi/userdata/addon_data/service.system.docker/config/certs.d" + ) + + // cleanPath is used to ensure that a directory name is valid on the target +diff -Naur a/registry/endpoint.go b/registry/endpoint.go +--- a/registry/endpoint.go 2016-02-11 19:45:56.000000000 +0100 ++++ b/registry/endpoint.go 2016-02-19 00:28:50.740085826 +0100 +@@ -73,7 +73,7 @@ + if endpoint.IsSecure { + // If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry` + // in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP. +- return fmt.Errorf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host) ++ return fmt.Errorf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /storage/.kodi/userdata/addon_data/service.system.docker/config/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host) + } + + // If registry is insecure and HTTPS failed, fallback to HTTP. diff --git a/packages/addons/service/docker/source/bin/docker-config b/packages/addons/service/docker/source/bin/docker-config new file mode 100755 index 0000000000..da5002b070 --- /dev/null +++ b/packages/addons/service/docker/source/bin/docker-config @@ -0,0 +1,37 @@ +#!/bin/sh +################################################################################ +# This file is part of LibreELEC - http://www.libreelec.tv +# Copyright (C) 2009-2016 Lukas Rusak (lrusak@libreelec.tv) +# +# LibreELEC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# LibreELEC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LibreELEC. If not, see . +################################################################################ + +ADDON_DIR="/storage/.kodi/addons/service.system.docker" +ADDON_HOME_DIR="/storage/.kodi/userdata/addon_data/service.system.docker" + +if [ ! -d "$ADDON_HOME_DIR/config" ]; then + mkdir -p $ADDON_HOME_DIR/config +fi + +if [ ! -f "$ADDON_HOME_DIR/config/docker.conf" ]; then + cp $ADDON_DIR/config/docker.conf $ADDON_HOME_DIR/config/docker.conf +fi + +if [ ! -d "$ADDON_HOME_DIR/docker" ]; then + mkdir -p $ADDON_HOME_DIR/docker +fi + +if [ ! -d /var/lib/docker ]; then + ln -sf $ADDON_HOME_DIR/docker /var/lib/docker +fi diff --git a/packages/addons/service/docker/source/config/docker.conf b/packages/addons/service/docker/source/config/docker.conf new file mode 100644 index 0000000000..36908b53d7 --- /dev/null +++ b/packages/addons/service/docker/source/config/docker.conf @@ -0,0 +1,2 @@ +DOCKER_DAEMON_OPTS="--graph=/storage/.kodi/userdata/addon_data/service.system.docker/docker" +DOCKER_STORAGE_OPTS="--storage-driver=overlay" diff --git a/packages/addons/service/docker/source/default.py b/packages/addons/service/docker/source/default.py new file mode 100644 index 0000000000..807ce9432b --- /dev/null +++ b/packages/addons/service/docker/source/default.py @@ -0,0 +1,103 @@ +################################################################################ +# This file is part of LibreELEC - http://www.libreelec.tv +# Copyright (C) 2009-2016 Lukas Rusak (lrusak@libreelec.tv) +# +# LibreELEC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# LibreELEC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LibreELEC. If not, see . +################################################################################ + +import os +import subprocess +import sys +import time +import xbmc +import xbmcaddon +import xbmcgui + +sys.path.append('/usr/share/kodi/addons/service.libreelec.settings') + +import oe + +__author__ = 'lrusak' +__addon__ = xbmcaddon.Addon() +__path__ = __addon__.getAddonInfo('path') +__service__ = __path__ + '/systemd/' + __addon__.getAddonInfo('id') + '.service' +__servicename__ = __addon__.getAddonInfo('id') + '.service' +__socket__ = __path__ + '/systemd/' + __addon__.getAddonInfo('id') + '.socket' +__socketname__ = __addon__.getAddonInfo('id') + '.socket' + +class Main(object): + + def __init__(self, *args, **kwargs): + + monitor = DockerMonitor(self) + + if not Docker().is_enabled(): + Docker().enable() + Docker().start() + + while not monitor.abortRequested(): + if monitor.waitForAbort(): + Docker().stop() + Docker().disable() + +class Docker(object): + + def enable(self): + self.execute('systemctl enable ' + __service__) + self.execute('systemctl enable ' + __socket__) + + def disable(self): + self.execute('systemctl disable ' + __servicename__) + self.execute('systemctl disable ' + __socketname__) + + def is_enabled(self): + if self.execute('systemctl is-enabled ' + __servicename__, get_result=1).strip('\n') == 'enabled': + return True + else: + return False + + def start(self): + self.execute('systemctl start ' + __servicename__) + + def stop(self): + self.execute('systemctl stop ' + __servicename__) + + def is_active(self): + if self.execute('systemctl is-active ' + __servicename__, get_result=1).strip('\n') == 'active': + return True + else: + return False + + def execute(self, command_line, get_result=0): + result = oe.execute(command_line, get_result=get_result) + if get_result: + return result + + def restart(self): + if self.is_active(): + self.stop() + self.start() + +class DockerMonitor(xbmc.Monitor): + + def __init__(self, *args, **kwargs): + xbmc.Monitor.__init__(self) + + def onSettingsChanged(self): + Docker().restart() + +if ( __name__ == "__main__" ): + Main() + + del DockerMonitor diff --git a/packages/addons/service/docker/source/examples/couchpotato.service b/packages/addons/service/docker/source/examples/couchpotato.service new file mode 100644 index 0000000000..3fd8b371f5 --- /dev/null +++ b/packages/addons/service/docker/source/examples/couchpotato.service @@ -0,0 +1,22 @@ +[Unit] +Description=%p container +Requires=service.system.docker.service +After=service.system.docker.service + +[Service] +Restart=always +RestartSec=10s +TimeoutStartSec=0 +ExecStartPre=-/bin/sh -c "mkdir -p /storage/%p/config /storage/%p/data" +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker run \ + --rm \ + --name=%p \ + --hostname=libreelec-%p \ + --volume=/storage/%p/config:/config \ + --volume=/storage/%p/data:/data \ + --publish=5050:5050 \ + timhaak/%p +ExecStop=/storage/.kodi/addons/service.system.docker/bin/docker stop %p + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/examples/mysql.service b/packages/addons/service/docker/source/examples/mysql.service new file mode 100644 index 0000000000..77b0667ed1 --- /dev/null +++ b/packages/addons/service/docker/source/examples/mysql.service @@ -0,0 +1,26 @@ +[Unit] +Description=%p container +Requires=service.system.docker.service +After=service.system.docker.service +Befora=kodi.service + +[Service] +Restart=always +RestartSec=10s +TimeoutStartSec=0 +ExecStartPre=-/bin/sh -c "mkdir -p /storage/%p/config /storage/%p/data" +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker run \ + --rm \ + --name=%p \ + --hostname=libreelec-%p \ + --volume=/storage/%p/config/:/etc/mysql/conf.d \ + --volume=/storage/%p/data:/var/lib/mysql \ + --publish=3306:3306 \ + --env=MYSQL_ROOT_PASSWORD=libreelec \ + --env=MYSQL_USER=kodi \ + --env=MYSQL_PASSWORD=kodi \ + %p +ExecStop=/storage/.kodi/addons/service.system.docker/bin/docker stop %p + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/examples/sabnzbd.service b/packages/addons/service/docker/source/examples/sabnzbd.service new file mode 100644 index 0000000000..e85e4da1b7 --- /dev/null +++ b/packages/addons/service/docker/source/examples/sabnzbd.service @@ -0,0 +1,22 @@ +[Unit] +Description=%p container +Requires=service.system.docker.service +After=service.system.docker.service + +[Service] +Restart=always +RestartSec=10s +TimeoutStartSec=0 +ExecStartPre=-/bin/sh -c "mkdir -p /storage/%p/config /storage/%p/data" +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker run \ + --rm \ + --name=%p \ + --hostname=libreelec-%p \ + --volume=/storage/%p/config:/config \ + --volume=/storage/%p/data:/data \ + --publish=8080:8080 \ + timhaak/%p +ExecStop=/storage/.kodi/addons/service.system.docker/bin/docker stop %p + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/examples/sickbeard.service b/packages/addons/service/docker/source/examples/sickbeard.service new file mode 100644 index 0000000000..573342bb92 --- /dev/null +++ b/packages/addons/service/docker/source/examples/sickbeard.service @@ -0,0 +1,22 @@ +[Unit] +Description=%p container +Requires=service.system.docker.service +After=service.system.docker.service + +[Service] +Restart=always +RestartSec=10s +TimeoutStartSec=0 +ExecStartPre=-/bin/sh -c "mkdir -p /storage/%p/config /storage/%p/data" +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker run \ + --rm \ + --name=%p \ + --hostname=libreelec-%p \ + --volume=/storage/%p/config:/config \ + --volume=/storage/%p/data:/data \ + --publish=8081:8081 \ + timhaak/%p +ExecStop=/storage/.kodi/addons/service.system.docker/bin/docker stop %p + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/examples/transmission.service b/packages/addons/service/docker/source/examples/transmission.service new file mode 100644 index 0000000000..7a248c6934 --- /dev/null +++ b/packages/addons/service/docker/source/examples/transmission.service @@ -0,0 +1,24 @@ +[Unit] +Description=%p container +Requires=service.system.docker.service +After=service.system.docker.service + +[Service] +Restart=always +RestartSec=10s +TimeoutStartSec=0 +ExecStartPre=-/bin/sh -c "mkdir -p /storage/%p/watch /storage/%p/downloads /storage/%p/incomplete /storage/%p/config" +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker run \ + --rm \ + --name=%p \ + --hostname=libreelec-%p \ + --volume=/storage/%p/watch:/watch \ + --volume=/storage/%p/downloads:/downloads \ + --volume=/storage/%p/incomplete:/incomplete \ + --volume=/storage/%p/config:/config \ + --publish=9091:9091 \ + timhaak/%p +ExecStop=/storage/.kodi/addons/service.system.docker/bin/docker stop %p + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/examples/var-lib-docker.mount b/packages/addons/service/docker/source/examples/var-lib-docker.mount new file mode 100644 index 0000000000..6744e125c3 --- /dev/null +++ b/packages/addons/service/docker/source/examples/var-lib-docker.mount @@ -0,0 +1,14 @@ +[Unit] +Description=Docker loopback mount +Requires=local-fs.target +Before=service.system.docker.service + +[Mount] +What=/storage/btrfs-loop.img +Where=/var/lib/docker + +Options=loop,compress=lzo +Type=btrfs + +[Install] +WantedBy=multi-user.target diff --git a/packages/addons/service/docker/source/systemd/service.system.docker.service b/packages/addons/service/docker/source/systemd/service.system.docker.service new file mode 100644 index 0000000000..0a2d5add32 --- /dev/null +++ b/packages/addons/service/docker/source/systemd/service.system.docker.service @@ -0,0 +1,19 @@ +[Unit] +Description=Docker Application Container Engine +Documentation=https://docs.docker.com +After=network.target docker.socket +Requires=docker.socket + +[Service] +Type=notify +ExecStartPre=/storage/.kodi/addons/service.system.docker/bin/docker-config +EnvironmentFile=-/storage/.kodi/userdata/addon_data/service.system.docker/config/docker.conf +ExecStart=/storage/.kodi/addons/service.system.docker/bin/docker daemon -H fd:// $DOCKER_DAEMON_OPTS $DOCKER_STORAGE_OPTS +MountFlags=slave +LimitNOFILE=1048576 +LimitNPROC=1048576 +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target +Alias=docker.service diff --git a/packages/addons/service/docker/source/systemd/service.system.docker.socket b/packages/addons/service/docker/source/systemd/service.system.docker.socket new file mode 100644 index 0000000000..578b38bc98 --- /dev/null +++ b/packages/addons/service/docker/source/systemd/service.system.docker.socket @@ -0,0 +1,13 @@ +[Unit] +Description=Docker Socket for the API +PartOf=docker.service + +[Socket] +ListenStream=/var/run/docker.sock +SocketMode=0660 +SocketUser=root +SocketGroup=root + +[Install] +WantedBy=sockets.target +Alias=docker.socket