cpg1111/maestro

View on GitHub
statecom/statecom.go

Summary

Maintainability
A
0 mins
Test Coverage
/*
Copyright 2016 Christian Grabowski All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package statecom

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "time"

    "github.com/cpg1111/maestro/config"
)

// State is the overall state
type State struct {
    Project    string
    Branch     string
    StateLabel string
    TimeStamp  time.Time
}

// ServiceState is the state of a service
type ServiceState struct {
    Name  string
    State *State
}

// StateCom is responsible for sending messages between Maestro and Maestrod for state
type StateCom struct {
    Project      string
    Branch       string
    Services     map[string]*ServiceStateMgr
    Global       *State
    client       *http.Client
    maestrodHost string
    maestrodPort int
}

func getMaestrodInfo(conf config.Project) (host string, port int) {
    if len(conf.MaestrodHostEnv) > 0 {
        host = os.Getenv(conf.MaestrodHostEnv)
    } else {
        host = conf.MaestrodHost
    }
    if len(conf.MaestrodPortEnv) > 0 {
        var err error
        port, err = strconv.Atoi(os.Getenv(conf.MaestrodPortEnv))
        if err != nil {
            panic(err)
        }
    } else {
        port = conf.MaestrodPort
    }
    return
}

// New returns a pointer to a StateCom struct
func New(conf config.Config, maestrodEndpoint, branch string) *StateCom {
    var client *http.Client
    if len(maestrodEndpoint) > 0 {
        client = &http.Client{}
    } else {
        client = nil
    }
    mdHost, mdPort := getMaestrodInfo(conf.Project)
    stateCom := &StateCom{
        Project: conf.Project.RepoURL,
        Branch:  branch,
        Global: &State{
            Project:    conf.Project.RepoURL,
            Branch:     branch,
            StateLabel: "pending",
            TimeStamp:  time.Now(),
        },
        client:       client,
        maestrodHost: mdHost,
        maestrodPort: mdPort,
    }
    stateCom.Services = make(map[string]*ServiceStateMgr)
    for i := 0; i < len(conf.Services); i++ {
        stateCom.Services[conf.Services[i].Name] = NewServiceStateMgr(conf.Services[i], stateCom)
    }
    return stateCom
}

func (s *StateCom) send(state interface{}) {
    payload, err := json.Marshal(state)
    if err != nil {
        log.Println("WARNING", err)
    }
    payloadRdr := bytes.NewReader(payload)
    resp, err := s.client.Post(
        fmt.Sprintf(
            "http://%s:%d/state",
            s.maestrodHost,
            s.maestrodPort,
        ),
        "application/json",
        payloadRdr,
    )
    if err != nil {
        log.Println("WARNING", err.Error())
    }
    if resp != nil && resp.StatusCode != 201 {
        log.Println("WARNING STATEUPDATE NOT SENT")
    }
}

// Send sends the messages out to maestrod
func (s *StateCom) Send(state interface{}, bkg bool) {
    if s.client != nil {
        if bkg {
            go s.send(state)
        } else {
            s.send(state)
        }
    }
}

func (s *StateCom) setState(state *State) {
    state.Project = s.Project
    state.Branch = s.Branch
    s.Send(state, true)
    s.Global = state
}

func (s *StateCom) updateState(state string) {
    s.setState(&State{
        StateLabel: state,
        TimeStamp:  time.Now(),
    })
}

// Start sets the state of the build to started
func (s *StateCom) Start() {
    s.updateState("started")
}

// Env sets the state of the build to
// creating the environment
func (s *StateCom) Env() {
    s.updateState("creating env")
}

// Cloning sets the state of the build to
// cloning repo
func (s *StateCom) Cloning() {
    s.updateState("cloning repo")
}

// CleanUp sets the state of the build to
// clean up
func (s *StateCom) CleanUp() {
    s.updateState("clean up")
}

type successPayload struct {
    Proj   string `json:"project"`
    Branch string `json:"branch"`
    Commit string `json:"commit"`
}

func (s *StateCom) success(commit string) {
    if s.client != nil {
        body := successPayload{
            Proj:   s.Project,
            Branch: s.Branch,
            Commit: commit,
        }
        s.Send(body, false)
    }
}

// Done sets the state of the build to
// done
func (s *StateCom) Done(success bool, commit string) {
    if success {
        s.success(commit)
    }
    s.updateState("done")
}

// SetServiceState sets the state of a specific service
func (s *StateCom) SetServiceState(srv *ServiceStateMgr) {
    srvState := &ServiceState{
        Name: srv.Name,
        State: &State{
            Project:    s.Project,
            Branch:     s.Project,
            StateLabel: srv.State,
            TimeStamp:  time.Now(),
        },
    }
    s.Send(srvState, true)
}