blob: 98ae93e544d1ad9564b218e77e10c03c9ce4ff65 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
package main
import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"time"
"v.io/jiri/tool"
"v.io/x/lib/cmdline"
)
// TODO(jsimsa): Add tests by mocking out jenkins.
//
// TODO(jsimsa): Create a tools/lib/gcutil package that encapsulates
// the interaction with GCE and use it here and in the vcloud tool.
func main() {
cmdline.Main(cmdVJenkins)
}
var cmdVJenkins = &cmdline.Command{
Name: "vjenkins",
Short: "Vanadium-specific utilities for interacting with Jenkins",
Long: `
Command vjenkins implements Vanadium-specific utilities for interacting with
Jenkins.
`,
Children: []*cmdline.Command{cmdNode},
}
var cmdNode = &cmdline.Command{
Name: "node",
Short: "Manage Jenkins slave nodes",
Long: "Manage Jenkins slave nodes.",
Children: []*cmdline.Command{cmdNodeCreate, cmdNodeDelete},
}
var cmdNodeCreate = &cmdline.Command{
Runner: cmdline.RunnerFunc(runNodeCreate),
Name: "create",
Short: "Create Jenkins slave nodes",
Long: `
Create Jenkins nodes. Uses the Jenkins REST API to create new slave nodes.
`,
ArgsName: "<names>",
ArgsLong: "<names> is a list of names identifying nodes to be created.",
}
var cmdNodeDelete = &cmdline.Command{
Runner: cmdline.RunnerFunc(runNodeDelete),
Name: "delete",
Short: "Delete Jenkins slave nodes",
Long: `
Delete Jenkins nodes. Uses the Jenkins REST API to delete existing slave nodes.
`,
ArgsName: "<names>",
ArgsLong: "<names> is a list of names identifying nodes to be deleted.",
}
var (
flagCredentialsId string
flagDescription string
flagJenkinsHost string
flagProject string
flagZone string
ipAddressRE = regexp.MustCompile(`^(\S*)\s*(\S*)\s(\S*)\s(\S*)\s(\S*)\s(\S*)$`)
)
func init() {
cmdVJenkins.Flags.StringVar(&flagJenkinsHost, "jenkins", "http://localhost:8080/jenkins", "The host of the Jenkins master.")
cmdNodeCreate.Flags.StringVar(&flagCredentialsId, "credentials-id", "73f76f53-8332-4259-bc08-d6f0b8521a5b", "The credentials ID used to connect the master to the node.")
cmdNodeCreate.Flags.StringVar(&flagDescription, "description", "", "Node description.")
cmdNodeCreate.Flags.StringVar(&flagZone, "zone", "us-central1-f", "GCE zone of the machine.")
cmdNodeCreate.Flags.StringVar(&flagProject, "project", "vanadium-internal", "GCE project of the machine.")
tool.InitializeRunFlags(&cmdVJenkins.Flags)
}
func newContext(env *cmdline.Env) *tool.Context {
return tool.NewContextFromEnv(env)
}
// lookupIPAddress looks up the IP address for the given GCE node.
func lookupIPAddress(ctx *tool.Context, node string) (string, error) {
var out bytes.Buffer
if err := ctx.NewSeq().Capture(&out, ctx.Stderr()).Last("gcloud", "compute", "instances",
"--project", flagProject,
"list", "--zones", flagZone, "-r", node, "--format=json"); err != nil {
return "", err
}
var instances []struct {
Name string
NetworkInterfaces []struct {
AccessConfigs []struct {
NatIP string
}
}
}
if err := json.Unmarshal(out.Bytes(), &instances); err != nil {
return "", fmt.Errorf("Unmarshal() failed: %v", err)
}
if len(instances) != 1 {
return "", fmt.Errorf("unexpected output:\n%v", out.String())
}
instance := instances[0]
return instance.NetworkInterfaces[0].AccessConfigs[0].NatIP, nil
}
// runNodeCreate adds slave node(s) to Jenkins configuration.
func runNodeCreate(env *cmdline.Env, args []string) error {
ctx := newContext(env)
jenkins, err := ctx.Jenkins(flagJenkinsHost)
if err != nil {
return err
}
for _, name := range args {
ipAddress, err := lookupIPAddress(ctx, name)
if err != nil {
return err
}
fmt.Println(ipAddress)
if err := jenkins.AddNodeToJenkins(name, ipAddress, flagDescription, flagCredentialsId); err != nil {
return err
}
}
return nil
}
// runNodeDelete removes slave node(s) from Jenkins configuration.
func runNodeDelete(env *cmdline.Env, args []string) error {
ctx := newContext(env)
jenkins, err := ctx.Jenkins(flagJenkinsHost)
if err != nil {
return err
}
for _, node := range args {
// Wait for the node to become idle.
const numRetries = 60
const retryPeriod = time.Minute
for i := 0; i < numRetries; i++ {
if ok, err := jenkins.IsNodeIdle(node); err != nil {
return err
} else if ok {
break
}
time.Sleep(retryPeriod)
}
err := jenkins.RemoveNodeFromJenkins(node)
if err != nil {
return err
}
}
return nil
}