mirror of
https://github.com/josmo/drone-rancher.git
synced 2026-06-04 18:24:22 +08:00
170 lines
4.5 KiB
Go
Executable File
170 lines
4.5 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
client "github.com/rancher/go-rancher/v2"
|
|
)
|
|
|
|
type Plugin struct {
|
|
URL string
|
|
Key string
|
|
Secret string
|
|
Service string
|
|
SidekickDockerImage []string
|
|
DockerImage string
|
|
StartFirst bool
|
|
Confirm bool
|
|
Timeout int
|
|
IntervalMillis int64
|
|
BatchSize int64
|
|
YamlVerified bool
|
|
}
|
|
|
|
func (p *Plugin) Exec() error {
|
|
log.Info("Drone Rancher Plugin built")
|
|
|
|
if p.URL == "" || p.Key == "" || p.Secret == "" || p.Service == "" {
|
|
return errors.New("Eek: Must have url, key, secret, and service definied")
|
|
}
|
|
|
|
var wantedService, wantedStack string
|
|
if strings.Contains(p.Service, "/") {
|
|
parts := strings.SplitN(p.Service, "/", 2)
|
|
wantedStack = parts[0]
|
|
wantedService = parts[1]
|
|
} else {
|
|
wantedService = p.Service
|
|
}
|
|
|
|
rancher, err := client.NewRancherClient(&client.ClientOpts{
|
|
Url: p.URL,
|
|
AccessKey: p.Key,
|
|
SecretKey: p.Secret,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to create rancher client: %s", err)
|
|
}
|
|
|
|
// Prepare service filters for service listing
|
|
serviceFilters := map[string]interface{}{"name": wantedService}
|
|
|
|
// Query stacks with filter name=wantedStack
|
|
if wantedStack != "" {
|
|
stacks, err := rancher.Stack.List(&client.ListOpts{Filters: map[string]interface{}{"name": wantedStack}})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to list rancher environments: %s", err)
|
|
}
|
|
if len(stacks.Data) <= 0 {
|
|
return fmt.Errorf("Unable to find stack %s", wantedStack)
|
|
}
|
|
// If found add stackID to serviceFilters
|
|
serviceFilters["stackId"] = stacks.Data[0].Id
|
|
|
|
}
|
|
|
|
// Query services with prepared filters
|
|
services, err := rancher.Service.List(&client.ListOpts{Filters: serviceFilters})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to list rancher services: %s", err)
|
|
}
|
|
if len(services.Data) <= 0 {
|
|
return fmt.Errorf("Unable to find service %s", p.Service)
|
|
}
|
|
service := services.Data[0]
|
|
// Service is found, proceed with upgrade
|
|
|
|
// We want to exit if there is no docker image updates
|
|
if p.DockerImage == "" && len(p.SidekickDockerImage) <= 0 {
|
|
return fmt.Errorf("Nothing to upgrade")
|
|
}
|
|
|
|
// Only change value if it's not null.
|
|
if p.DockerImage != "" {
|
|
// Add prefix when missing to meet Rancher API requirement
|
|
service.LaunchConfig.ImageUuid = prepareDockerPrefix(p.DockerImage)
|
|
}
|
|
|
|
// Iterate over provided sidekick flags
|
|
for _, sidekick := range p.SidekickDockerImage {
|
|
// Split flag in two from "--sidekick nginx nginx:latest"
|
|
parts := strings.SplitN(sidekick, " ", 2)
|
|
wantedSidekick := parts[0]
|
|
wantedImage := parts[1]
|
|
for i, s := range service.SecondaryLaunchConfigs {
|
|
if wantedSidekick == s.Name {
|
|
service.SecondaryLaunchConfigs[i].ImageUuid = prepareDockerPrefix(wantedImage)
|
|
}
|
|
}
|
|
}
|
|
upgrade := &client.ServiceUpgrade{}
|
|
upgrade.InServiceStrategy = &client.InServiceUpgradeStrategy{
|
|
LaunchConfig: service.LaunchConfig,
|
|
SecondaryLaunchConfigs: service.SecondaryLaunchConfigs,
|
|
StartFirst: p.StartFirst,
|
|
IntervalMillis: p.IntervalMillis,
|
|
BatchSize: p.BatchSize,
|
|
}
|
|
upgrade.ToServiceStrategy = &client.ToServiceUpgradeStrategy{}
|
|
_, err = rancher.Service.ActionUpgrade(&service, upgrade)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to upgrade service %s: %s", p.Service, err)
|
|
}
|
|
|
|
if p.Confirm {
|
|
srv, err := retry(func() (interface{}, error) {
|
|
s, e := rancher.Service.ById(service.Id)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if s.State != "upgraded" {
|
|
return nil, fmt.Errorf("Service not upgraded: %s", s.State)
|
|
}
|
|
return s, nil
|
|
}, time.Duration(p.Timeout)*time.Second, 3*time.Second)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Error waiting for service upgrade to complete: %s", err)
|
|
}
|
|
|
|
_, err = rancher.Service.ActionFinishupgrade(srv.(*client.Service))
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to finish upgrade %s: %s", p.Service, err)
|
|
}
|
|
log.Infof("Finished upgrade %s", p.Service)
|
|
}
|
|
|
|
log.Infof("Upgraded %s to %s", p.Service, p.DockerImage)
|
|
|
|
return nil
|
|
}
|
|
|
|
type retryFunc func() (interface{}, error)
|
|
|
|
func prepareDockerPrefix(image string) string {
|
|
if !strings.HasPrefix(image, "docker:") {
|
|
image = fmt.Sprintf("docker:%s", image)
|
|
}
|
|
return image
|
|
}
|
|
|
|
func retry(f retryFunc, timeout time.Duration, interval time.Duration) (interface{}, error) {
|
|
finish := time.After(timeout)
|
|
for {
|
|
result, err := f()
|
|
if err == nil {
|
|
return result, nil
|
|
}
|
|
select {
|
|
case <-finish:
|
|
return nil, err
|
|
case <-time.After(interval):
|
|
}
|
|
}
|
|
}
|