From cb883bc5280851121e8db2ad6fdc19ebc374f887 Mon Sep 17 00:00:00 2001 From: Tim Shannon Date: Wed, 30 Mar 2016 21:48:52 +0000 Subject: [PATCH] Fleshed out how projects will be loaded and run Started on datastores --- LICENSE | 2 +- datastore/ds.go | 6 +++ main.go | 16 ++++-- project.go | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 datastore/ds.go diff --git a/LICENSE b/LICENSE index 472ac23..c8b48cc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ MIT License -Copyright (c) +Copyright (c) 2016 Tim Shannon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/datastore/ds.go b/datastore/ds.go new file mode 100644 index 0000000..f597128 --- /dev/null +++ b/datastore/ds.go @@ -0,0 +1,6 @@ +// Copyright 2016 Tim Shannon. All rights reserved. +// Use of this source code is governed by the MIT license +// that can be found in the LICENSE file. + +// Package datastore manages the bolt data files and the reading and writing data to them +package datastore diff --git a/main.go b/main.go index 242cc05..aeec536 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,7 @@ +// Copyright 2016 Tim Shannon. All rights reserved. +// Use of this source code is governed by the MIT license +// that can be found in the LICENSE file. + package main import ( @@ -10,8 +14,8 @@ import ( //settings var ( - projectDir = "./projects" // /etc/ - dataDir = "./data" + projectDir = "./projects" // /etc/ironsmith/ + dataDir = "./data" // /var/ironsmith/ address = "http://localhost:8026" certFile = "" keyFile = "" @@ -37,12 +41,12 @@ func main() { keyFile = cfg.String("keyFile", keyFile) //prep dirs - err = os.MkdirAll(filepath.Join(projectDir, enabledProjectDir), os.ModeDir) + err = os.MkdirAll(filepath.Join(projectDir, enabledProjectDir), 0777) if err != nil { log.Fatalf("Error Creating project directory at %s: %s", projectDir, err) } - err = os.MkdirAll(dataDir, os.ModeDir) + err = os.MkdirAll(dataDir, 0777) if err != nil { log.Fatalf("Error Creating project data directory at %s: %s", dataDir, err) } @@ -53,5 +57,9 @@ func main() { } //load projects + err = projects.load() + if err != nil { + log.Fatalf("Error loading projects: %s", err) + } //start server } diff --git a/project.go b/project.go index de9585c..89e5640 100644 --- a/project.go +++ b/project.go @@ -1,14 +1,31 @@ +// Copyright 2016 Tim Shannon. All rights reserved. +// Use of this source code is governed by the MIT license +// that can be found in the LICENSE file. + package main import ( "encoding/json" + "fmt" + "io/ioutil" "os" "path/filepath" + "strings" + "sync" + "time" ) const enabledProjectDir = "enabled" // Project is an ironsmith project that contains how to fetch, build, test, and release a project +/* +The project lifecycle goes like this, each step calling the next if successful + (Load Project file) -> (Fetch) -> (Build) -> (Test) -> (Release) - > (Sleep for polling period) -> + (Reload Project File) -> (Fetch) -> etc... + + Changes the project file will be reloaded on every poll / trigger + If a project file is deleted then the cycle will finish it's current poll and stop at the load phase +*/ type Project struct { Name string `json:"name"` // name of the project @@ -22,6 +39,9 @@ type Project struct { ReleaseFile string `json:"releaseFile"` PollInterval string `json:"pollInterval"` // if not poll interval is specified, this project is trigger only TriggerSecret string `json:"triggerSecret"` //secret to be included with a trigger call + + filename string + poll time.Duration } const projectTemplateFilename = "template.project.json" @@ -69,3 +89,118 @@ func prepTemplateProject() error { return nil } + +func (p *Project) load() error { + if p.filename == "" { + return fmt.Errorf("Invalid project file name") + } + + if !projects.exists(p.filename) { + // project has been deleted + // don't continue polling + // TODO: Clean up Project data folder? + return nil + } + + data, err := ioutil.ReadFile(filepath.Join(projectDir, enabledProjectDir, p.filename)) + if err != nil { + return err + } + err = json.Unmarshal(data, p) + if err != nil { + return err + } + + err = p.prepData() + if err != nil { + return err + } + + if p.PollInterval != "" { + p.poll, err = time.ParseDuration(p.PollInterval) + if err != nil { + //TODO: Log pollInterval parse failure in project store + p.poll = 0 + } + } + + if p.poll > 0 { + //start polling + } + + return nil +} + +// prepData makes sure the project's data folder and data store is created +/* + folder structure + projectDataFolder// + +*/ +func (p *Project) prepData() error { + var dirName = strings.TrimSuffix(p.filename, filepath.Ext(p.filename)) + err := os.MkdirAll(filepath.Join(dataDir, dirName), 0777) + if err != nil { + return err + } + + //TODO: Create data store +} + +type projectList struct { + sync.RWMutex + data map[string]*Project +} + +var projects = projectList{ + data: make(map[string]*Project), +} + +func (p *projectList) load() error { + p.Lock() + defer p.Unlock() + + dir, err := os.Open(filepath.Join(projectDir, enabledProjectDir)) + defer func() { + if cerr := dir.Close(); cerr != nil && err == nil { + err = cerr + } + }() + + if err != nil { + return err + } + + files, err := dir.Readdir(0) + if err != nil { + return err + } + + for i := range files { + if !files[i].IsDir() && filepath.Ext(files[i].Name()) == ".json" { + prj := &Project{filename: files[i].Name()} + p.data[files[i].Name()] = prj + + err = prj.load() + if err != nil { + delete(p, files[i].Name()) + return err + } + } + } + + return nil +} + +func (p *projectList) exists(name string) bool { + p.RLock() + defer p.RUnlock() + + _, ok := p.data[name] + return ok +} + +// startProjectLoader polls for new projects +func startProjectLoader() { + +}