Skip to content

Commit

Permalink
Merge pull request #2 from DeFacto-Team/develop
Browse files Browse the repository at this point in the history
Alpha release
  • Loading branch information
ilzheev authored Sep 15, 2020
2 parents 1e7a0dd + 20b3ea9 commit d6cd790
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 1 deletion.
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,62 @@
# Factom Network Restart System
Restart system for Factom network
The app connects to remote Portainer server via Portainer API and asynchronously restarts all containers with label `name=factomd`.

## Configuration
Config file with Portainer credentials should be placed in `~/.factom-restart/config.yaml` or provided as a flag `-c /path/to/config.yaml` while running the application.

## Usage
### Dry-run
Run in dry-run mode (scans Portainer endpoints and containers, no restart happens):
```bash
go run .
```
Output:
```bash
2020/09/15 13:49:01 Using config: /Users/anton/.factom-restart/config.yaml
2020/09/15 13:49:01 Starting restart system
2020/09/15 13:49:01 Portainer endpoint: https://test.com
2020/09/15 13:49:01 DRY RUN mode: simulating restart
2020/09/15 13:49:01 Successfully logged in as anton
------------------------------
2020/09/15 13:49:01 Trying connecting to X (xxx.xxx.xxx.xxx), ID=2
2020/09/15 13:49:01 Empty response received from Portainer API
------------------------------
2020/09/15 13:49:02 Trying connecting to Y (yyy.yyy.yyy.yyy), ID=29
2020/09/15 13:49:02 factomd container is running
307f871a97e1dd91851c6298cf3a183f1709d908810b0059ce9094622bb78126
v6.6.0-alpine
------------------------------
2020/09/15 13:49:05 SIMULATING RESTART (DRY-RUN)
2020/09/15 13:49:05 SKIP X
2020/09/15 13:49:05 OK Y (307f871a97e1dd91851c6298cf3a183f1709d908810b0059ce9094622bb78126)
```

### Live mode
Run in live mode (restarts the network):
```bash
go run . --live
```
Output:
```bash
2020/09/15 13:49:01 Using config: /Users/anton/.factom-restart/config.yaml
2020/09/15 13:49:01 Starting restart system
2020/09/15 13:49:01 Portainer endpoint: https://test.com
2020/09/15 13:49:01 LIVE mode: network will be restarted
2020/09/15 13:49:01 Successfully logged in as anton
------------------------------
2020/09/15 13:49:01 Trying connecting to X (xxx.xxx.xxx.xxx), ID=2
2020/09/15 13:49:01 Empty response received from Portainer API
------------------------------
2020/09/15 13:49:02 Trying connecting to Y (yyy.yyy.yyy.yyy), ID=29
2020/09/15 13:49:02 factomd container is running
307f871a97e1dd91851c6298cf3a183f1709d908810b0059ce9094622bb78126
v6.6.0-alpine
------------------------------
2020/09/15 13:49:05 RESTARTING NOW
2020/09/15 13:49:05 SKIP X
2020/09/15 13:49:05 OK Y (307f871a97e1dd91851c6298cf3a183f1709d908810b0059ce9094622bb78126)
```
3 changes: 3 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
endpoint: https://test.factom.com
username: test
password: test
25 changes: 25 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"github.com/jinzhu/configor"
"github.com/mcuadros/go-defaults"
)

// App config struct
type Config struct {
Endpoint string `default:"" json:"endpoint" form:"endpoint" query:"endpoint" required:"true"`
Username string `default:"" json:"username" form:"username" query:"username" required:"true"`
Password string `default:"" json:"password" form:"password" query:"password" required:"true"`
}

// Create config from configFile
func NewConfig(configFile string) (*Config, error) {

config := new(Config)
defaults.SetDefaults(config)

if err := configor.Load(config, configFile); err != nil {
return nil, err
}
return config, nil
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/DeFacto-Team/factom-network-restart

go 1.14

require (
github.com/jinzhu/configor v1.2.0
github.com/mcuadros/go-defaults v1.2.0
)
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/jinzhu/configor v1.2.0 h1:u78Jsrxw2+3sGbGMgpY64ObKU4xWCNmNRJIjGVqxYQA=
github.com/jinzhu/configor v1.2.0/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
138 changes: 138 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import (
"flag"
"log"
"os/user"
"path/filepath"
"strings"
"sync"
)

const (
ColorRed = "\033[31m"
ColorReset = "\033[0m"
ColorYellow = "\033[33m"
ColorBlue = "\033[34m"
)

func main() {

var live bool
var conf *Config

usr, err := user.Current()
if err != nil {
log.Println(err)
}

// default config location
configFile := filepath.Join(usr.HomeDir, ".factom-restart/config.yaml")

// check if custom config location passed as flag
flag.StringVar(&configFile, "c", configFile, "config.yaml path")

// parse dry run flag
flag.BoolVar(&live, "live", false, "live bool")

flag.Parse()

log.Printf("Using config: %s\n", configFile)

// load config
if conf, err = NewConfig(configFile); err != nil {
log.Fatal(err)
}

log.Printf("Starting restart system\n")
log.Printf("Portainer endpoint: %s\n", conf.Endpoint)

if live {
log.Println(string(ColorYellow), "LIVE mode: network will be restarted!", string(ColorReset))
} else {
log.Println(string(ColorBlue), "DRY RUN mode: simulating restart", string(ColorReset))
}

// initialize portainer
p := NewPortainer(conf.Username, conf.Password, conf.Endpoint)

// get all endpoints from portainer
endpoints, err := p.GetSwarmEndpoints()
if err != nil {
log.Fatal(err)
}

// get factomd containers for each endpoint
for i := range endpoints {
if endpoints[i].PublicURL != "" {
log.Println("------------------------------")
log.Printf("Trying connecting to %s (%s), ID=%d\n", endpoints[i].Name, endpoints[i].PublicURL, endpoints[i].ID)
endpoints[i].Containers, err = p.GetDockerContainers(endpoints[i].ID)
if err != nil {
log.Println(string(ColorRed), err, string(ColorReset))
}
for j := range endpoints[i].Containers {
version := strings.Split(endpoints[i].Containers[j].Image, ":")
if endpoints[i].Containers[j].State == "running" {
log.Printf("factomd container is %s\n%s\n%s\n", endpoints[i].Containers[j].State, endpoints[i].Containers[j].ID, version[1])
} else {
log.Printf("factomd container is %s\n%s\n%s\n", endpoints[i].Containers[j].State, endpoints[i].Containers[j].ID, version[1])
}
}
}
}

// RESTART START
log.Println("------------------------------")
if live {
log.Println("RESTARTING NOW")
} else {
log.Println("SIMULATING RESTART (DRY-RUN)")
}

var wg sync.WaitGroup

// Restart each factomd container
for _, e := range endpoints {
if e.PublicURL != "" {

if len(e.Containers) > 0 {
// for all online hosts with containers, restart factomd container(s)
for _, c := range e.Containers {
if live {
// if live mode, async restarting
wg.Add(1)
go func(name string, endpointID int, containerID string) {
restart(p, name, endpointID, containerID)
wg.Done()
}(e.Name, e.ID, c.ID)
} else {
// if dry-run mode, just print endpoint and containerId
log.Printf("OK %s (%s)\n", e.Name, c.ID)
}
}
} else {
// log skipped endpoints with no containers
log.Printf("SKIP %s", e.Name)
}

}
}

wg.Wait()

}

func restart(p *Portainer, name string, endpointID int, containerID string) {

err := p.RestartDockerContainer(endpointID, containerID)
if err != nil {
// if restart request failed, print error
log.Printf("ERROR %s (%s)\n", name, containerID)
log.Println(string(ColorRed), err, string(ColorReset))
} else {
// no error = success restart, print OK
log.Printf("OK %s (%s)\n", name, containerID)
}

}
Loading

0 comments on commit d6cd790

Please sign in to comment.