Commit 2a33a8c1 authored by Addshore's avatar Addshore 🏄 Committed by Addshore
Browse files

mwdd: mediawiki create & destroy.

Working starters for mwdd solution with implemented create and
destroy commands.

Change-Id: I84b000c0c1d0e3ee873e1a755476c25bda693167
parent 438b2ba3
......@@ -54,4 +54,7 @@ unit:
test: unit lint
internal/mwdd/files/files.go: static/mwdd/*
staticfiles -o internal/mwdd/files/files.go static/mwdd/
.PHONY: install release
......@@ -39,6 +39,14 @@ No naming structured is enforced in CI but a convention exists that should be fo
- Complex sub commands will be split out into their own file. For example `docker_env.go`.
- This is a recursive solution.
### Updating included static files
Static files are included in the binary using <https://github.com/bouk/staticfiles>
You can install the staticfiles command with `go get bou.ke/staticfiles`
In order to update files.go you can run `make internal/mwdd/files/files.go`
### Using a binary
Make a binary by running `make install`
......
......@@ -61,9 +61,9 @@ var dockerStartCmd = &cobra.Command{
Verbosity: Verbosity,
HandleError: handlePortError,
}
exec.RunCommand(options, docker.ComposeCommand("up", "-d"))
MediaWiki := mediawikiOrFatal()
exec.RunCommand(options, exec.DockerComposeCommand("up", "-d"))
if isLinuxHost() {
fileCreated,err := docker.EnsureDockerComposeUserOverrideExists()
......@@ -184,9 +184,9 @@ var dockerExecCmd = &cobra.Command{
if NoTTY {
args = append([]string{"-T"}, args...)
exec.RunCommand(options, exec.DockerComposeCommand("exec", args...))
exec.RunCommand(options, docker.ComposeCommand("exec", args...))
} else {
exec.RunTTYCommand(options, exec.DockerComposeCommand("exec", args...))
exec.RunTTYCommand(options, docker.ComposeCommand("exec", args...))
}
},
......@@ -203,7 +203,7 @@ var dockerDestroyCmd = &cobra.Command{
}
runArgs := append([]string{"-sfv"}, args...)
exec.RunTTYCommand(options, exec.DockerComposeCommand("rm", runArgs...))
exec.RunTTYCommand(options, docker.ComposeCommand("rm", runArgs...))
if len(args) == 0 || contains(args, "mediawiki") {
MediaWiki.RenameLocalSettings()
......@@ -236,7 +236,7 @@ var dockerStopCmd = &cobra.Command{
Spinner: Spinner,
Verbosity: Verbosity,
}
exec.RunCommand(options, exec.DockerComposeCommand("stop"))
exec.RunCommand(options, docker.ComposeCommand("stop"))
},
}
......@@ -250,7 +250,7 @@ var dockerStatusCmd = &cobra.Command{
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
exec.RunCommand(options, exec.DockerComposeCommand("ps"))
exec.RunCommand(options, docker.ComposeCommand("ps"))
},
}
......@@ -263,7 +263,7 @@ func printSuccess() {
strings.Replace(stdout.String(), "0.0.0.0", "localhost", 1))
},
}
exec.RunCommand(options, exec.DockerComposeCommand("port", "mediawiki", "8080"))
exec.RunCommand(options, docker.ComposeCommand("port", "mediawiki", "8080"))
}
......
......@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mwdd"
"fmt"
"github.com/spf13/cobra"
......@@ -41,7 +42,14 @@ var mwddCreateCmd = &cobra.Command{
Use: "create",
Short: "Create the Default containers",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
mwdd.DefaultForUser().EnsureReady()
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
// TODO mediawiki should come from some default definition set?
mwdd.DefaultForUser().UpDetached( []string{"mediawiki"}, options )
// TODO add functionality for writing to the hosts file...
//mwdd.DefaultForUser().EnsureHostsFile()
},
}
......@@ -49,7 +57,11 @@ var mwddDestroyCmd = &cobra.Command{
Use: "destroy",
Short: "Destroy the Default containers",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
mwdd.DefaultForUser().EnsureReady()
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
mwdd.DefaultForUser().DownWithVolumesAndOrphans( options )
},
}
......
......@@ -19,6 +19,9 @@ package cmd
import (
"fmt"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mwdd"
"github.com/spf13/cobra"
)
......@@ -28,11 +31,34 @@ var mwddMediawikiCmd = &cobra.Command{
RunE: nil,
}
var mwddMediawikiInstallCmd = &cobra.Command{
Use: "install",
Short: "Installs a new MediaWiki site using install.php",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
},
}
var mwddMediawikiComposerCmd = &cobra.Command{
Use: "composer",
Short: "Runs composer in a container in the context of MediaWiki",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
},
}
var mwddMediawikiCreateCmd = &cobra.Command{
Use: "create",
Short: "Create the Mediawiki containers",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
mwdd.DefaultForUser().EnsureReady()
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
// TODO mediawiki should come from some default definition set?
mwdd.DefaultForUser().UpDetached( []string{"mediawiki"}, options )
// TODO add functionality for writing to the hosts file...
//mwdd.DefaultForUser().EnsureHostsFile()
},
}
......@@ -40,8 +66,11 @@ var mwddMediawikiDestroyCmd = &cobra.Command{
Use: "destroy",
Short: "Destroy the Mediawiki containers",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Not yet implemented!");
},
mwdd.DefaultForUser().EnsureReady()
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
mwdd.DefaultForUser().DownWithVolumesAndOrphans( options ) },
}
var mwddMediawikiSuspendCmd = &cobra.Command{
......@@ -66,4 +95,6 @@ func init() {
mwddMediawikiCmd.AddCommand(mwddMediawikiDestroyCmd)
mwddMediawikiCmd.AddCommand(mwddMediawikiSuspendCmd)
mwddMediawikiCmd.AddCommand(mwddMediawikiResumeCmd)
mwddMediawikiCmd.AddCommand(mwddMediawikiInstallCmd)
mwddMediawikiCmd.AddCommand(mwddMediawikiComposerCmd)
}
......@@ -18,15 +18,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package docker
import (
"os"
"path/filepath"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
"os"
osexec "os/exec"
)
/*ComposeCommand ...*/
func ComposeCommand (command string, arg ...string) *osexec.Cmd {
projectDir, _ := os.Getwd()
context := exec.ComposeCommandContext{
ProjectDirectory: projectDir,
ProjectName: "mw-" + filepath.Base(projectDir),
}
return exec.ComposeCommand(
context,
"exec",
"-T",
"mediawiki",
"/bin/bash",
"/docker/install.sh",
)
}
/*MediaWikiInstall ...*/
func MediaWikiInstall( options exec.HandlerOptions ) {
exec.RunCommand(
options,
exec.DockerComposeCommand(
ComposeCommand(
"exec",
"-T",
"mediawiki",
......@@ -39,7 +59,7 @@ func MediaWikiInstall( options exec.HandlerOptions ) {
func MediaWikiComposerUpdate( options exec.HandlerOptions ) {
exec.RunCommand(
options,
exec.DockerComposeCommand(
ComposeCommand(
"exec",
"-T",
"mediawiki",
......@@ -50,7 +70,7 @@ func MediaWikiComposerUpdate( options exec.HandlerOptions ) {
func mediaWikiPHPVersionCheck( options exec.HandlerOptions ) error {
return exec.RunCommand(options,
exec.DockerComposeCommand(
ComposeCommand(
"exec",
"-T",
"mediawiki",
......
......@@ -23,7 +23,6 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"github.com/briandowns/spinner"
)
......@@ -36,16 +35,26 @@ type HandlerOptions struct {
HandleError func(stderr bytes.Buffer, err error)
}
// ComposeCommandContext ...
type ComposeCommandContext struct {
ProjectDirectory string
ProjectName string
Files []string
}
/*Command passes through to exec.Command for running generic commands*/
func Command(name string, arg ...string) *exec.Cmd {
return exec.Command(name, arg...)
}
/*DockerComposeCommand gets a docker-compose command to run*/
func DockerComposeCommand(command string, arg ...string) *exec.Cmd {
projectDir, _ := os.Getwd()
projectName := "mw-" + filepath.Base(projectDir)
arg = append([]string{"-p", projectName, command}, arg...)
/*ComposeCommand gets a docker-compose command to run*/
func ComposeCommand(context ComposeCommandContext, command string, arg ...string) *exec.Cmd {
arg = append([]string{command}, arg...)
arg = append([]string{"--project-name", context.ProjectName}, arg...)
arg = append([]string{"--project-directory", context.ProjectDirectory}, arg...)
for _, element := range context.Files {
arg = append( []string {"--file", context.ProjectDirectory + "/" + element }, arg... )
}
return exec.Command("docker-compose", arg...)
}
......
package files
import (
"fmt"
"io/ioutil"
)
func packagedFileToBytes(file string) []byte {
fileReader, err := Open(file)
if err != nil {
fmt.Println(err)
}
bytes, _ := ioutil.ReadAll(fileReader)
return bytes
}
/*packagedFileNames*/
func packagedFileNames() []string {
keys := make([]string, 0, len(staticFiles))
for k := range staticFiles {
keys = append(keys, k)
}
return keys
}
func filter(ss []string, test func(string) bool) (ret []string) {
for _, s := range ss {
if test(s) {
ret = append(ret, s)
}
}
return
}
\ No newline at end of file
package files
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
)
/*EnsureInMemoryFilesAreOnDisk makes sure that up to date copies of our in app docker-compose files are on disk
TODO this should be called only when we update the bin?
TODO This is way to complex, and should be more clever.. checking a hash or something? and be more lightweight*/
func ensureInMemoryFilesAreOnDisk( projectDirectory string ) {
packagedFiles := packagedFileNames()
// Ensure each file is on disk and up to date
for _, file := range packagedFiles {
fileTargetOnDisk := projectDirectory + string(os.PathSeparator) + file
packagedBytes := packagedFileToBytes(file)
if _, err := os.Stat(fileTargetOnDisk); os.IsNotExist(err) {
// TODO only output the below line with verbose logging
//fmt.Println(fileTargetOnDisk + " doesn't exist, so write it...")
writeBytesToDisk(packagedBytes, fileTargetOnDisk)
} else {
onDiskBytes := diskFileToBytes(fileTargetOnDisk)
if(!bytes.Equal(onDiskBytes, packagedBytes)) {
// TODO only output the below line with verbose logging
//fmt.Println(fileTargetOnDisk + " out of date, so writing...")
writeBytesToDisk(packagedBytes, fileTargetOnDisk)
}
}
}
}
func diskFileToBytes(file string) []byte {
bytes, _ := ioutil.ReadFile(file)
// TODO check error?
return bytes
}
func getAssumedFilePerms( filePath string ) os.FileMode {
// Set all .sh files as +x when creating them
if filepath.Ext(filePath) == ".sh" {
return 0700
}
return 0600
}
func writeBytesToDisk( bytes []byte, filePath string ) {
ensureDirectoryForFileOnDisk(filePath)
ioutil.WriteFile(filePath, bytes, getAssumedFilePerms(filePath))
// TODO check error?
}
func ensureDirectoryForFileOnDisk(file string) {
ensureDirectoryOnDisk(filepath.Dir(file))
}
func ensureDirectoryOnDisk(dirPath string) {
if _, err := os.Stat(dirPath); err != nil {
os.MkdirAll(dirPath, 0700)
}
}
\ No newline at end of file
package files
import (
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)
type staticFilesFile struct {
data string
mime string
mtime time.Time
// size is the size before compression. If 0, it means the data is uncompressed
size int
// hash is a sha256 hash of the file contents. Used for the Etag, and useful for caching
hash string
}
var staticFiles = map[string]*staticFilesFile{
"base.yml": {
data: "\x1f\x8b\b\x00\x00\x00\x00\x00\x02\xff\x8c\x93Oo\xdb<\f\xc6\xef\xfe\x14D\xfb\x02\xbd4v\x1a\xf4d \x87\x02}\x81\rؖ\xa0\xf1v5T\x89I\xb4H\xa4 \xc9v\xbaa\xdf}\x90\xf3\xc7n\x91\x0e\x85\x0f2\xa4\xdfC>\x14\xc5\x16}\xd0L%\xdc\xcc\xf2\xd9M\x96\x05\xf4\xad\x96\x18\xca,\x03P.\x94\x19\x00\x80\xb6b\x83%(\\{\xd4Q\x84BQ\x988\xcf\xfb\x97I\x12\xa0節Mc\xf1(\x01\x98@\xd1\n_\xf8\x86\n\xc5r\x87>\x0f,w\xe5\xa5\xcd^\xb1\xe5\x10IؔDž\xdcvJ冥0i\xbf\a\bc\xc7~wNp\xb6\xd7[t\xed}-\x94\xf2\x18B\tw\xd3<}w\xd3T\x06m4\xed\x0fv\x0f\xfc5T\x8b\xc7E\t\x1e\x9d\x11\x12\xa1\xd3q\v?;m\x14\xfabD\xdf\x02\x93Dh\x9c\x12\x11\xd5\xf8&\x826\xad\xa0\xce*\x1c\xf3\xa5\x11\x11\x8ff\x91Z\xed\x99,R\x1c.\xe4\xc7\xe7\xa7\xea\xfb×\xfa\xd3bU\xcd{ɥ:\x13\x99\x88o\x0f_\xff_\xcds\x8bJ\x8bN\xef\xf4\x1b\xf6\x80^C\xf2-\x85W\x90.\x0f<\x066M\xd4L\xb7\x10\xb7\x82v\x01\"\xc3\xe3r5\x84\xae\xaae\xbd\\<U\xf3\xff~\xa7\xe5\xcf)\x90\xa6\x88\x9e\x84\x01\xc7\xfe\xe0$\xfd\x8c\xfayu\x14\x94\xc7\xf5\xaa?Q\xe8\x90T\xa8\x99\x06R\xb9\xf0\xa6\xa7\xefV\xabh\x94a\xe8\xdb\xf1\xa8\x0e(\xbc\xdc\x0e\xc4\a\xdeŐ\xfe\x83/2Z\xf7j\xc3\xf3Y\x90\x1f\xfa[H\xa3\x91bmž~f\xf5R\a\xfd\vsɴ.\v\x8c\xf2\x041\xads\xf5\x0f\xf6B\xe0\xa8-r\x13\xc3{\xc1^\x9f{βq\xb9Ä:aOe&Ro\x86ј@h\x9e\t\xe3y,\xa6\xc5\xec\xfeo\x00\x00\x00\xff\xff\x9bص4\xf9\x03\x00\x00",
hash: "032234f69f1e96b97a1feed3ad86c7d93c8d6745689cdc06688be5606777a5c1",
mime: "",
mtime: time.Unix(1616180366, 0),
size: 1017,
},
"mediawiki.yml": {
data: "\x1f\x8b\b\x00\x00\x00\x00\x00\x02\xff\x84\x93_o\xda0\x14\xc5\xdf\xf3)\xae\xbaI\x95&\xd9\x06\xa6m\xc8\x12\x0f\xace4\x1a\x94\xaa@\xbb\xb7ȍ/\x89Eb{\xb6I\xa8\xa6~\xf7)\xb4\r\u007f:u\xe2ɾ\xe7\xfe\x8e\xef\xe1\xa6B\xe7\x95\xd1\x1c\xce{\xb4w\x1eE\x1e]\xa5R\xf4<\x8a\x00J\x94J\xd4j\xadx\x04\x00\xa0J\x91!\ai\xd25:\xe20S>\xb8G\xda\bvJj\\\xc6$V\xcc\a\x87\xcd\xed\xb7\x1e\b+\xd2\x1c{d+\xf1a\x93\xf1\x0e\xfdJ\xbb\xc4ww\xd8\xca\x14\x9b\xb2ql\x0e@\x80\xb2Z\xa8@V\xc6\x11\x15\xa8\xcf\xf9\xe9ٙ\xbdt\xffDV\xd6R\x1e\xd4\xce>\xfe\x99\x8e.\xe3\xe1}\xfc3N\xeef\x93\xe5t4O.f\x97\xa3'\xce*\xe1X]\xd7,\x0fe\xc1j\x9e6\x8f\x93g\xefv\xc6\xd3\xe1x4\xe7\xa45$\xbb8\xfc\x1b\x1a{\xbeg\xcf1q\x89\x05f\"\xfc\x0f?\x99\x8d\x8f\xe0\x85\xc9\xc9\x0e\xc6<ᡮ\x943\xbaD\x1d^\x02\x84\x0f\xb0\xf4(\xe1\xe1\x11*\xe1\x94\xd9x(\x85\xd2\x01\xb5\xd0)\x82O\x9d\xb2\xc1C0\xb0RZ´!߫\xb5\xa2m\xfb\xb0\xf0\x06\x1c\xfe\xde(\x87\x12V\xc6A;\xa2\xd2\x12\xb7\xd4\xe6\x16\b\xe4!X\xcf\x19\xb3\xb9xp*\x15\xc1\xb8\x93]Xt\xbf|\xee\xf7{/`\x02\xd3\xfb$\xbe\x9e/\x86\x93Ir3\\\\\rN\x92kuw\xf1\xedb9\x9c$W\xb3\xf9b\xf0\x89\xb6\xc3\xd3\xe6/\xa6\x85IE\x91\x1b\x1f\xde\xe8of\xb7\x8bA\xbf\xd3ﴕ_\x97\xa3\xef\xcbqr1\xbb\xfe\x11\x8f\a\x87\xc1\x1fU\x9ev\r\rS\x8b\x129\xbc\xeb\x96>1\x9a\xb7>:SzK\xac3\xdb\xc7g\x8d\xf6\xfbb\xb7C\x9b_\xb7\xf3ZJ<\n\x97\xe6{\xc5?\\4\x86ڸ\xf5\x01FZ\x1fE\a\x1f\xcb\xe9&\x1e\xdf5\v\xc4\xff\x06\x00\x00\xff\xff)\x8aR.\xde\x03\x00\x00",
hash: "f2f6863aa5cc909f3bbc1cc8c888275e7a2c93a642fb3e33a4cc651f79252faf",
mime: "",
mtime: time.Unix(1616180351, 0),
size: 990,
},
"nginx/client_max_body_size.conf": {
data: "client_max_body_size 1024m;",
hash: "87e1334f7ba74c1f53d7fb2b1b035b14cb6105e94dce8c5d75303595cdc91831",
mime: "",
mtime: time.Unix(1616179541, 0),
size: 0,
},
"nginx/timeouts.conf": {
data: "\x1f\x8b\b\x00\x00\x00\x00\x00\x02\xff*(ʯ\xa8\x8cO\xce\xcf\xcbKM.\x89/\xc9\xccM\xcd/-Q042(\xb6\xe6\x82\xc8\x15\xa7\xe6\xa5\xc0%\x14\x14P\xe4\x8aR\x131\xe5\x00\x01\x00\x00\xff\xffp\x17(\x9eS\x00\x00\x00",
hash: "431b33796adb902aa6dfaa1d01bc7cf0365511d7c89b923a5066f529c5bc2958",
mime: "",
mtime: time.Unix(1616179541, 0),
size: 83,
},
"wait-for-it.sh": {
data: "\x1f\x8b\b\x00\x00\x00\x00\x00\x02\xff\xa4W[S\xdb\xc8\x12\xe8\b\x1d\x8e͉\"\x9b:\xe7l\xad\x89Ȳ\xc0\x82\xabH`\xb1\xfd\xb0EQ\x94,\xb5\xd0\x14\xf2\x8c23\xe2\x12\x87\xfd\xed[s\xd1\xcd8\x89\x93\xe8\xc5\xd2\\z\xbe\xee\xfe\xe6\xeb\xf6֫\xa0\x14<\x98\x13\x1a \xbd\x87y$2g\v\x00f\x02AfD\x80\x889)$H\x06\x12\x85\x04\x92B\x04\xb7\xe4\x1e)L\x0f/ cB\x06\x05\xe3\x12\"\x8e\x10\xddG$\x8f\xe69:N\xbcHh\xb4\xc0\xd0\xeb\xcd#\x81\xea\x15\xbcA\xdfq0\xce\x18r\xde\xeb\xc3\xba\x02\xef\xcf\xd9\xf8x\n>E\x18\xc2\xf5\xf5\x1e\xc8\f)\xa8u\xe0z\xbf\xb90\xdc\xdf\xde݃\x94\xc0\xb3\xe3\x94\"\xba\xc5^\xdfY:\x00\x00q$\xe1\xed[\x98M\x0eN\x8ea\u007f\xa9\xf3,\x00\x8dp\xa4\x11^\xf9\xe2\x1a\xae|\t\x92,\x90\x95R}\xf8\x10\xb3\xc5\"\xa2\tD\xfcV\\\xeb\xad~\x06\xa7\xe7\x93)|\x06\xdfW\xbbC\xfde\x9eS&$0\x0e\xe3\v(i\x82\\\a\xc5\xec*\xe0\xe2\xfc\xd2\xec\xfa\xcb<*R\x1a\xc2ʞ\xaf=\a\xb9DN#I\xee1\u007fz\rO\xac\x04Q`L\xd2'\x15 \xed\x16(\xdc&\xf8\xa2\xf1Ӡ\x11\x1a\x88\x90\x9c\xc4r\xc5\xf2\x00\x1f1.%\x82(\xe7U\x04H\xaa-\xeb4\x8b2\x8e\x11\x13a\x8c}\xd4\xc6>\x96\x04Wm\xc1\x11\xa3\xff\x96\xc0JY\x94\n\xcf\x13\b\x19\xc9R\xc0\x02\x85J\x865 a:~\u007f|>3\xe1\xb1\xf1\x0f\xed\xd87C15\xeb\x81P\x10\x183\x9a\x88\xd7\xf0\t9\x83\x94q\xa0\xacʧ9ʇ\xc3\xf3\xf7\xef\x0f>\x1c\xc1\xc1\xe5ɤc\xe7\xd8\xfa\\9\xfc@d\xa6\xf3\x0eQ*U^*\xefSB\x89\xc8P8\x9aZ\xda.>\x12\tC\xe7\xd9q\x1e\"\"oR\xc6k\x1aZ\x1aW\x1e\xfa\xb7\x12\x065\x91k\xe7,\xf1\xc1\xad\x989\x02e\x89\xd0[톧H6\xf24i\f\xae\xca-`ic\xdc\xfa\xef\x1aL\xb9\xc0\x1f\xb5\xaf\f\xd7G\x18s)\xd1?BF\\\xdeH\x11z\xbd$\x92\b\xff\xf9\x97\xe8\x8c\xe4\b\xe6z%\xac>غ?\x9e\xfc>\x9b\xfc\x05>~l]\xe3Nji\f\xfe'\x03\x044\x90\xce,GQ\xe62\xf4\xde5\x1e\xb5\xddSOO\x8b\xc2>\x04\t\xde\a2.\x02m+ж\xfa\xb0\xaf\x87i\x99\xbf=\xfc\x86q\xebl\xcb\x01\xb3F;0X\xef\x00\xd2d]\\\xbe\x92\x81vԉh\xf4\xd1\x12\xce\xeb\xf5\x8cM\xf0\xeb\xb0\xf7\xfb\xdd$WϜct\xb7\x0e\xbe\xc8\x11\v\x18ڴP\x132\x8e\xb2\xe4\xb4r\xaa\a\x1e\x15\x056\xf4݂1\x05Ƶ01\x10e\xa1\x15e2>\x19\u007f\x98BRr\xc5 ˓\x11dR\x16\xa3 ()y|#d\x14\xdf\xe1c\x9cE\xf4\x16\xdf\xc4l\x11D\xc1\xff~\xf9\xff\xaf\xbb\xed[a\xc5}=+*\x86{\x8a:\x8a\xe2\u007f\x9c\x1d\x9c4d\xf7\x06\xb5\xea\xf8~\x9c\x91<\xa9\x14ِ\xc8\n\xad\to#+\xf5\xfe헷d\x93\x13\u007f\xf8$\x9b\x94\x8b\xf1Q\xe8\xbdү\x92G\x05\xb8w$\xcf\xc1W\xe1\xf4\xbd\x8b\xf1\x91\v\xe3\x0f\x86\xfb*'\xa0\x86\xf4\xd7\xe5\xf1dv6\xadHj\xe3g\x06uu\xdcLTjՈ\xe3\x92sL,\xd5*-XU\x92Uq\xe8HA\xc5\"\x03B\xb1h\v\n\xceb\x14B\xe9f\xb9@*\x85ctA\x81ݪ\xc5ϱ\x02\x11G\x02\xc1\xf5\x86.\x90\x06\xf5\xceh\a\x9a\xab\xa3¬\xa3\xdb\xf3\x96\xc3 \x18\x05\xf0\xdcL*`\xa1\xb7\xac\xd6\\\r\xae\x9f\xeb9\x05\xb7=7l͉\x8c\xa4\x12\x1a\x11\xd8۫_m\x82\x9b3\x0eO\xc7gG\xe1p\xa3\xbd\xadJ\xd8\x18\xd0\x1c\xdf\xd0@\xbb.7\x16&\xd3\xcb\xf1\xe1\xa6&\xb2\x95\xf0\xb8ޮ\xbb*f\x9a\xb5a\b\xae۴TZA\xf6:ҡO\xd9]\x1f%M\xff\x9d\x17g-\x87[;\u1cfb\x11Ң\xdfM\xd6:\xa4\xfaJ\xfd\x1c\x9d\x17g}\x0f\xd2V.\xec\x05Y\v\xb6\xba<?\x87\xb7\x92\x8f\x9du\x87~\x0fj\xbf\xdf]\xd6P\xfal\x1c\xaa\xe6\xd9Y_=\xba\x95'\xdda\xaf[\xd8\x02[\xabΌ\xdeQ\xf6@k-\x18\x817t\xbfn\nE\x14;\xbaL9&\xa6\xae\xe6\xaakC\xfa\xf93\xb8F\x87Vb\xect\x0e>\xe6\x9c\xf1\x91n\x8a)b\xa2\nW\xc1\xd9=I\x10\xa2\x95\xd6\xd8\xfeqycp\x19L)q\x9c*\xe2\xdeҾ\x8d\xfc\xdd\xff\x0e\x9e\x1d{\x15\xbd\xa5y\x19\xf9\x83g\xc7(\x84\xb7Կz\xc4\\yo\xa9\u007f\xf5\x88\xb3\x05q\x86\xf1\x9d.\xa2\x88\xba\x9b\xae\xfaV\x01)g\v\x98\x97\xe2i\xce\x1e\xdf}\xcfR\v\xee\xe6\xe2`z\x1az=\x8eQ^D2\x03\xaf\xf7\x90\x918\xab6\xf6\xfbN\x97\xa2z\x03\x84\u007f\x83kM\xb9/\xab\x87i\xd9Z\xaa\xd3.\x8a\xa1\xebK\xd7\xe9\x14O\xbb~\xf0\x85\xf5\xae\x8e\xab\x85\xa1C\xb5\xae\x17\xae\xfa\x905%O\xf7\xd8U\xb1\xa9\xbd^moꉕ#\xda\xfet\xa0\xbc\\\x9b\x92\x8eCgcx\xf5\x92\x92\xeb\xca\xf4\xf66x\x86=_j|\xd6\xd4m\xfbWm\xc1\x12|\r\x1c\xd3R\xe8Ƌ\xb5\xff\xa9\xd9\xda\xdb\\\xb1N\xc4Z\x85[\xedѐ\x9b0v\x96\xa6\xe4\x9f\x00\x00\x00\xff\xff\xaf\xabL\xf5\xf8\x0f\x00\x00",
hash: "6a2b7c49ead02dcaf820d25c8df99043aca0c0937a38c0c1a75725e0faa42326",
mime: "text/x-sh; charset=utf-8",
mtime: time.Unix(1616179541, 0),
size: 4088,
},
}
// NotFound is called when no asset is found.
// It defaults to http.NotFound but can be overwritten
var NotFound = http.NotFound
// ServeHTTP serves a request, attempting to reply with an embedded file.
func ServeHTTP(rw http.ResponseWriter, req *http.Request) {
path := strings.TrimPrefix(req.URL.Path, "/")
f, ok := staticFiles[path]
if !ok {
if path != "" && !strings.HasSuffix(path, "/") {
NotFound(rw, req)
return
}
f, ok = staticFiles[path+"index.html"]
if !ok {
NotFound(rw, req)
return
}
}
header := rw.Header()
if f.hash != "" {
if hash := req.Header.Get("If-None-Match"); hash == f.hash {
rw.WriteHeader(http.StatusNotModified)
return
}
header.Set("ETag", f.hash)
}
if !f.mtime.IsZero() {
if t, err := time.Parse(http.TimeFormat, req.Header.Get("If-Modified-Since")); err == nil && f.mtime.Before(t.Add(1*time.Second)) {
rw.WriteHeader(http.StatusNotModified)
return
}
header.Set("Last-Modified", f.mtime.UTC().Format(http.TimeFormat))
}
header.Set("Content-Type", f.mime)
// Check if the asset is compressed in the binary
if f.size == 0 {
header.Set("Content-Length", strconv.Itoa(len(f.data)))
io.WriteString(rw, f.data)
} else {
if header.Get("Content-Encoding") == "" && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
header.Set("Content-Encoding", "gzip")
header.Set("Content-Length", strconv.Itoa(len(f.data)))
io.WriteString(rw, f.data)
} else {
header.Set("Content-Length", strconv.Itoa(f.size))
reader, _ := gzip.NewReader(strings.NewReader(f.data))
io.Copy(rw, reader)
reader.Close()
}
}
}
// Server is simply ServeHTTP but wrapped in http.HandlerFunc so it can be passed into net/http functions directly.
var Server http.Handler = http.HandlerFunc(ServeHTTP)
// Open allows you to read an embedded file directly. It will return a decompressing Reader if the file is embedded in compressed format.
// You should close the Reader after you're done with it.
func Open(name string) (io.ReadCloser, error) {
f, ok := staticFiles[name]
if !ok {
return nil, fmt.Errorf("Asset %s not found", name)
}
if f.size == 0 {
return ioutil.NopCloser(strings.NewReader(f.data)), nil
}
return gzip.NewReader(strings.NewReader(f.data))
}
// ModTime returns the modification time of the original file.
// Useful for caching purposes
// Returns zero time if the file is not in the bundle
func ModTime(file string) (t time.Time) {
if f, ok := staticFiles[file]; ok {
t = f.mtime
}
return
}
// Hash returns the hex-encoded SHA256 hash of the original file
// Used for the Etag, and useful for caching
// Returns an empty string if the file is not in the bundle
func Hash(file string) (s string) {
if f, ok := staticFiles[file]; ok {
s = f.hash
}
return
}
/*Package files for interacting packaged files and their counterparts on disk for a project directory
Copyright © 2020 Addshore
This program 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 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package files
import (
"os"
"path/filepath"
)
/*EnsureReady makes sure that the files component is ready*/
func EnsureReady( projectDirectory string ) {
ensureInMemoryFilesAreOnDisk( projectDirectory );
}
/*ListRawDcYamlFilesInContextOfProjectDirectory ...*/
func ListRawDcYamlFilesInContextOfProjectDirectory(projectDirectory string) []string {
// TODO this function should live in the mwdd struct?
var files []string
for _, file := range listRawFiles(projectDirectory) {
if( filepath.Ext(file) == ".yml" ) {
files = append(files, filepath.Base(file))
}
}
return files
}
/*listRawFiles lists the raw docker-compose file paths that are currently on disk*/
func listRawFiles(projectDirectory string) []string {
var files []string
err := filepath.Walk(projectDirectory, func(path string, info os.FileInfo, err error) error {
if(! info.IsDir()){
files = append(files, path)
}
return nil
})
if err != nil {
panic(err)
}
return files
}
\ No newline at end of file
......@@ -18,13 +18,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package mwdd
import (
"os/user"
"fmt"