func buildGoogleComputeService(cfg *config.ProviderConfig) (*compute.Service, error) { if !cfg.IsSet("ACCOUNT_JSON") { return nil, fmt.Errorf("missing ACCOUNT_JSON") } a, err := loadGoogleAccountJSON(cfg.Get("ACCOUNT_JSON")) if err != nil { return nil, err } config := jwt.Config{ Email: a.ClientEmail, PrivateKey: []byte(a.PrivateKey), Scopes: []string{ compute.DevstorageFullControlScope, compute.ComputeScope, }, TokenURL: "https://accounts.google.com/o/oauth2/token", } client := config.Client(oauth2.NoContext) if gceCustomHTTPTransport != nil { client.Transport = gceCustomHTTPTransport } return compute.New(client) }
func newLocalProvider(cfg *config.ProviderConfig) (Provider, error) { scriptsDir, _ := os.Getwd() if cfg.IsSet("SCRIPTS_DIR") { scriptsDir = cfg.Get("SCRIPTS_DIR") } if scriptsDir == "" { scriptsDir = os.TempDir() } return &localProvider{cfg: cfg, scriptsDir: scriptsDir}, nil }
func buildJupiterBrainImageSelector(selectorType string, cfg *config.ProviderConfig) (image.Selector, error) { switch selectorType { case "env": return image.NewEnvSelector(cfg) case "api": baseURL, err := url.Parse(cfg.Get("IMAGE_SELECTOR_URL")) if err != nil { return nil, err } return image.NewAPISelector(baseURL), nil default: return nil, fmt.Errorf("invalid image selector type %q", selectorType) } }
func gceTestSetupSSH(t *testing.T, cfg *config.ProviderConfig) { var ( td string err error ) if !cfg.IsSet("TEMP_DIR") { td, err = ioutil.TempDir("", "travis-worker") if err != nil { t.Fatal(err) } cfg.Set("TEMP_DIR", td) } td = cfg.Get("TEMP_DIR") keyPath := filepath.Join(td, "test_rsa") pubKeyPath := filepath.Join(td, "test_rsa.pub") err = ioutil.WriteFile(keyPath, []byte(gceTestSSHKey), 0644) if err != nil { t.Fatal(err) } err = ioutil.WriteFile(pubKeyPath, []byte(gceTestSSHPubKey), 0644) if err != nil { t.Fatal(err) } if !cfg.IsSet("SSH_KEY_PATH") { cfg.Set("SSH_KEY_PATH", keyPath) } if !cfg.IsSet("SSH_PUB_KEY_PATH") { cfg.Set("SSH_PUB_KEY_PATH", pubKeyPath) } if !cfg.IsSet("SSH_KEY_PASSPHRASE") { cfg.Set("SSH_KEY_PASSPHRASE", gceTestSSHKeyPassphrase) } }
func newDockerProvider(cfg *config.ProviderConfig) (Provider, error) { client, err := buildDockerClient(cfg) if err != nil { return nil, err } cpuSetSize := runtime.NumCPU() if cpuSetSize < 2 { cpuSetSize = 2 } privileged := false if cfg.IsSet("PRIVILEGED") { privileged = (cfg.Get("PRIVILEGED") == "true") } cmd := []string{"/sbin/init"} if cfg.IsSet("CMD") { cmd = strings.Split(cfg.Get("CMD"), " ") } memory := uint64(1024 * 1024 * 1024 * 4) if cfg.IsSet("MEMORY") { if parsedMemory, err := humanize.ParseBytes(cfg.Get("MEMORY")); err == nil { memory = parsedMemory } } cpus := uint64(2) if cfg.IsSet("CPUS") { if parsedCPUs, err := strconv.ParseUint(cfg.Get("CPUS"), 10, 64); err == nil { cpus = parsedCPUs } } return &dockerProvider{ client: client, runPrivileged: privileged, runCmd: cmd, runMemory: memory, runCPUs: int(cpus), cpuSets: make([]bool, cpuSetSize), }, nil }
func buildDockerClient(cfg *config.ProviderConfig) (*docker.Client, error) { // check for both DOCKER_ENDPOINT and DOCKER_HOST, the latter for // compatibility with docker's own env vars. if !cfg.IsSet("ENDPOINT") && !cfg.IsSet("HOST") { return nil, ErrMissingEndpointConfig } endpoint := cfg.Get("ENDPOINT") if endpoint == "" { endpoint = cfg.Get("HOST") } if cfg.IsSet("CERT_PATH") { path := cfg.Get("CERT_PATH") ca := fmt.Sprintf("%s/ca.pem", path) cert := fmt.Sprintf("%s/cert.pem", path) key := fmt.Sprintf("%s/key.pem", path) return docker.NewTLSClient(endpoint, cert, key, ca) } return docker.NewClient(endpoint) }
func newBlueBoxProvider(cfg *config.ProviderConfig) (Provider, error) { return &blueBoxProvider{ client: goblueboxapi.NewClient(cfg.Get("CUSTOMER_ID"), cfg.Get("API_KEY")), cfg: cfg, }, nil }
func newJupiterBrainProvider(cfg *config.ProviderConfig) (Provider, error) { if !cfg.IsSet("ENDPOINT") { return nil, ErrMissingEndpointConfig } if !cfg.IsSet("IMAGE_ALIASES") { return nil, fmt.Errorf("expected IMAGE_ALIASES config key") } aliasNames := cfg.Get("IMAGE_ALIASES") baseURL, err := url.Parse(cfg.Get("ENDPOINT")) if err != nil { return nil, err } aliasNamesSlice := strings.Split(aliasNames, ",") imageAliases := make(map[string]string, len(aliasNamesSlice)) for _, aliasName := range aliasNamesSlice { normalizedAliasName := strings.ToUpper(string(nonAlphaNumRegexp.ReplaceAll([]byte(aliasName), []byte("_")))) key := fmt.Sprintf("IMAGE_ALIAS_%s", normalizedAliasName) if !cfg.IsSet(key) { return nil, fmt.Errorf("expected image alias %q", aliasName) } imageAliases[aliasName] = cfg.Get(key) } if !cfg.IsSet("SSH_KEY_PATH") { return nil, fmt.Errorf("expected SSH_KEY_PATH config key") } sshKeyPath := cfg.Get("SSH_KEY_PATH") if !cfg.IsSet("SSH_KEY_PASSPHRASE") { return nil, fmt.Errorf("expected SSH_KEY_PASSPHRASE config key") } sshKeyPassphrase := cfg.Get("SSH_KEY_PASSPHRASE") if !cfg.IsSet("KEYCHAIN_PASSWORD") { return nil, fmt.Errorf("expected KEYCHAIN_PASSWORD config key") } keychainPassword := cfg.Get("KEYCHAIN_PASSWORD") bootPollSleep := 3 * time.Second if cfg.IsSet("BOOT_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("BOOT_POLL_SLEEP")) if err != nil { return nil, err } bootPollSleep = si } return &jupiterBrainProvider{ client: http.DefaultClient, baseURL: baseURL, imageAliases: imageAliases, sshKeyPath: sshKeyPath, sshKeyPassphrase: sshKeyPassphrase, keychainPassword: keychainPassword, bootPollSleep: bootPollSleep, }, nil }
func newJupiterBrainProvider(cfg *config.ProviderConfig) (Provider, error) { if !cfg.IsSet("ENDPOINT") { return nil, ErrMissingEndpointConfig } baseURL, err := url.Parse(cfg.Get("ENDPOINT")) if err != nil { return nil, err } if !cfg.IsSet("SSH_KEY_PATH") { return nil, fmt.Errorf("expected SSH_KEY_PATH config key") } sshKeyPath := cfg.Get("SSH_KEY_PATH") if !cfg.IsSet("SSH_KEY_PASSPHRASE") { return nil, fmt.Errorf("expected SSH_KEY_PASSPHRASE config key") } sshKeyPassphrase := cfg.Get("SSH_KEY_PASSPHRASE") if !cfg.IsSet("KEYCHAIN_PASSWORD") { return nil, fmt.Errorf("expected KEYCHAIN_PASSWORD config key") } keychainPassword := cfg.Get("KEYCHAIN_PASSWORD") bootPollSleep := 3 * time.Second if cfg.IsSet("BOOT_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("BOOT_POLL_SLEEP")) if err != nil { return nil, err } bootPollSleep = si } imageSelectorType := defaultJupiterBrainImageSelectorType if cfg.IsSet("IMAGE_SELECTOR_TYPE") { imageSelectorType = cfg.Get("IMAGE_SELECTOR_TYPE") } imageSelector, err := buildJupiterBrainImageSelector(imageSelectorType, cfg) if err != nil { return nil, err } return &jupiterBrainProvider{ client: http.DefaultClient, baseURL: baseURL, sshKeyPath: sshKeyPath, sshKeyPassphrase: sshKeyPassphrase, keychainPassword: keychainPassword, bootPollSleep: bootPollSleep, imageSelectorType: imageSelectorType, imageSelector: imageSelector, }, nil }
func newGCEProvider(cfg *config.ProviderConfig) (Provider, error) { var ( imageSelector image.Selector err error ) client, err := buildGoogleComputeService(cfg) if err != nil { return nil, err } if !cfg.IsSet("PROJECT_ID") { return nil, fmt.Errorf("missing PROJECT_ID") } projectID := cfg.Get("PROJECT_ID") if !cfg.IsSet("SSH_KEY_PATH") { return nil, fmt.Errorf("missing SSH_KEY_PATH config key") } sshKeyBytes, err := ioutil.ReadFile(cfg.Get("SSH_KEY_PATH")) if err != nil { return nil, err } if !cfg.IsSet("SSH_PUB_KEY_PATH") { return nil, fmt.Errorf("missing SSH_PUB_KEY_PATH config key") } sshPubKeyBytes, err := ioutil.ReadFile(cfg.Get("SSH_PUB_KEY_PATH")) if err != nil { return nil, err } block, _ := pem.Decode(sshKeyBytes) if block == nil { return nil, fmt.Errorf("ssh key does not contain a valid PEM block") } if !cfg.IsSet("SSH_KEY_PASSPHRASE") { return nil, fmt.Errorf("missing SSH_KEY_PASSPHRASE config key") } der, err := x509.DecryptPEMBlock(block, []byte(cfg.Get("SSH_KEY_PASSPHRASE"))) if err != nil { return nil, err } parsedKey, err := x509.ParsePKCS1PrivateKey(der) if err != nil { return nil, err } sshKeySigner, err := ssh.NewSignerFromKey(parsedKey) if err != nil { return nil, err } zoneName := defaultGCEZone if cfg.IsSet("ZONE") { zoneName = cfg.Get("ZONE") } cfg.Set("ZONE", zoneName) mtName := defaultGCEMachineType if cfg.IsSet("MACHINE_TYPE") { mtName = cfg.Get("MACHINE_TYPE") } cfg.Set("MACHINE_TYPE", mtName) nwName := defaultGCENetwork if cfg.IsSet("NETWORK") { nwName = cfg.Get("NETWORK") } cfg.Set("NETWORK", nwName) diskSize := defaultGCEDiskSize if cfg.IsSet("DISK_SIZE") { ds, err := strconv.ParseInt(cfg.Get("DISK_SIZE"), 10, 64) if err == nil { diskSize = ds } } bootPollSleep := defaultGCEBootPollSleep if cfg.IsSet("BOOT_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("BOOT_POLL_SLEEP")) if err != nil { return nil, err } bootPollSleep = si } uploadRetries := defaultGCEUploadRetries if cfg.IsSet("UPLOAD_RETRIES") { ur, err := strconv.ParseUint(cfg.Get("UPLOAD_RETRIES"), 10, 64) if err != nil { return nil, err } uploadRetries = ur } uploadRetrySleep := defaultGCEUploadRetrySleep if cfg.IsSet("UPLOAD_RETRY_SLEEP") { si, err := time.ParseDuration(cfg.Get("UPLOAD_RETRY_SLEEP")) if err != nil { return nil, err } uploadRetrySleep = si } defaultLanguage := defaultGCELanguage if cfg.IsSet("DEFAULT_LANGUAGE") { defaultLanguage = cfg.Get("DEFAULT_LANGUAGE") } defaultImage := defaultGCEImage if cfg.IsSet("IMAGE_DEFAULT") { defaultImage = cfg.Get("IMAGE_DEFAULT") } autoImplode := true if cfg.IsSet("AUTO_IMPLODE") { ai, err := strconv.ParseBool(cfg.Get("AUTO_IMPLODE")) if err != nil { return nil, err } autoImplode = ai } hardTimeoutMinutes := defaultGCEHardTimeoutMinutes if cfg.IsSet("HARD_TIMEOUT_MINUTES") { ht, err := strconv.ParseInt(cfg.Get("HARD_TIMEOUT_MINUTES"), 10, 64) if err != nil { return nil, err } hardTimeoutMinutes = ht } imageSelectorType := defaultGCEImageSelectorType if cfg.IsSet("IMAGE_SELECTOR_TYPE") { imageSelectorType = cfg.Get("IMAGE_SELECTOR_TYPE") } if imageSelectorType != "legacy" && imageSelectorType != "env" && imageSelectorType != "api" { return nil, fmt.Errorf("invalid image selector type %q", imageSelectorType) } if imageSelectorType == "env" || imageSelectorType == "api" { imageSelector, err = buildGCEImageSelector(imageSelectorType, cfg) if err != nil { return nil, err } } return &gceProvider{ client: client, projectID: projectID, cfg: cfg, ic: &gceInstanceConfig{ DiskSize: diskSize, SSHKeySigner: sshKeySigner, SSHPubKey: string(sshPubKeyBytes), AutoImplode: autoImplode, HardTimeoutMinutes: hardTimeoutMinutes, }, imageSelector: imageSelector, imageSelectorType: imageSelectorType, instanceGroup: cfg.Get("INSTANCE_GROUP"), bootPollSleep: bootPollSleep, defaultLanguage: defaultLanguage, defaultImage: defaultImage, uploadRetries: uploadRetries, uploadRetrySleep: uploadRetrySleep, }, nil }
func newGCEProvider(cfg *config.ProviderConfig) (Provider, error) { var ( imageSelector image.Selector err error ) client, err := buildGoogleComputeService(cfg) if err != nil { return nil, err } if !cfg.IsSet("PROJECT_ID") { return nil, fmt.Errorf("missing PROJECT_ID") } projectID := cfg.Get("PROJECT_ID") imageProjectID := cfg.Get("PROJECT_ID") if cfg.IsSet("IMAGE_PROJECT_ID") { imageProjectID = cfg.Get("IMAGE_PROJECT_ID") } zoneName := defaultGCEZone if cfg.IsSet("ZONE") { zoneName = cfg.Get("ZONE") } cfg.Set("ZONE", zoneName) mtName := defaultGCEMachineType if cfg.IsSet("MACHINE_TYPE") { mtName = cfg.Get("MACHINE_TYPE") } cfg.Set("MACHINE_TYPE", mtName) premiumMTName := defaultGCEPremiumMachineType if cfg.IsSet("PREMIUM_MACHINE_TYPE") { premiumMTName = cfg.Get("PREMIUM_MACHINE_TYPE") } cfg.Set("PREMIUM_MACHINE_TYPE", premiumMTName) nwName := defaultGCENetwork if cfg.IsSet("NETWORK") { nwName = cfg.Get("NETWORK") } cfg.Set("NETWORK", nwName) diskSize := defaultGCEDiskSize if cfg.IsSet("DISK_SIZE") { ds, err := strconv.ParseInt(cfg.Get("DISK_SIZE"), 10, 64) if err == nil { diskSize = ds } } bootPollSleep := defaultGCEBootPollSleep if cfg.IsSet("BOOT_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("BOOT_POLL_SLEEP")) if err != nil { return nil, err } bootPollSleep = si } bootPrePollSleep := defaultGCEBootPrePollSleep if cfg.IsSet("BOOT_PRE_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("BOOT_PRE_POLL_SLEEP")) if err != nil { return nil, err } bootPrePollSleep = si } stopPollSleep := defaultGCEStopPollSleep if cfg.IsSet("STOP_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("STOP_POLL_SLEEP")) if err != nil { return nil, err } stopPollSleep = si } stopPrePollSleep := defaultGCEStopPrePollSleep if cfg.IsSet("STOP_PRE_POLL_SLEEP") { si, err := time.ParseDuration(cfg.Get("STOP_PRE_POLL_SLEEP")) if err != nil { return nil, err } stopPrePollSleep = si } skipStopPoll := false if cfg.IsSet("SKIP_STOP_POLL") { ssp, err := strconv.ParseBool(cfg.Get("SKIP_STOP_POLL")) if err != nil { return nil, err } skipStopPoll = ssp } uploadRetries := defaultGCEUploadRetries if cfg.IsSet("UPLOAD_RETRIES") { ur, err := strconv.ParseUint(cfg.Get("UPLOAD_RETRIES"), 10, 64) if err != nil { return nil, err } uploadRetries = ur } uploadRetrySleep := defaultGCEUploadRetrySleep if cfg.IsSet("UPLOAD_RETRY_SLEEP") { si, err := time.ParseDuration(cfg.Get("UPLOAD_RETRY_SLEEP")) if err != nil { return nil, err } uploadRetrySleep = si } defaultLanguage := defaultGCELanguage if cfg.IsSet("DEFAULT_LANGUAGE") { defaultLanguage = cfg.Get("DEFAULT_LANGUAGE") } defaultImage := defaultGCEImage if cfg.IsSet("IMAGE_DEFAULT") { defaultImage = cfg.Get("IMAGE_DEFAULT") } autoImplode := true if cfg.IsSet("AUTO_IMPLODE") { ai, err := strconv.ParseBool(cfg.Get("AUTO_IMPLODE")) if err != nil { return nil, err } autoImplode = ai } imageSelectorType := defaultGCEImageSelectorType if cfg.IsSet("IMAGE_SELECTOR_TYPE") { imageSelectorType = cfg.Get("IMAGE_SELECTOR_TYPE") } if imageSelectorType != "env" && imageSelectorType != "api" { return nil, fmt.Errorf("invalid image selector type %q", imageSelectorType) } if imageSelectorType == "env" || imageSelectorType == "api" { imageSelector, err = buildGCEImageSelector(imageSelectorType, cfg) if err != nil { return nil, err } } var rateLimiter ratelimit.RateLimiter if cfg.IsSet("RATE_LIMIT_REDIS_URL") { rateLimiter = ratelimit.NewRateLimiter(cfg.Get("RATE_LIMIT_REDIS_URL"), cfg.Get("RATE_LIMIT_PREFIX")) } else { rateLimiter = ratelimit.NewNullRateLimiter() } rateLimitMaxCalls := defaultGCERateLimitMaxCalls if cfg.IsSet("RATE_LIMIT_MAX_CALLS") { mc, err := strconv.ParseUint(cfg.Get("RATE_LIMIT_MAX_CALLS"), 10, 64) if err != nil { return nil, err } rateLimitMaxCalls = mc } rateLimitDuration := defaultGCERateLimitDuration if cfg.IsSet("RATE_LIMIT_DURATION") { rld, err := time.ParseDuration(cfg.Get("RATE_LIMIT_DURATION")) if err != nil { return nil, err } rateLimitDuration = rld } privKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, err } pubKey, err := ssh.NewPublicKey(&privKey.PublicKey) if err != nil { return nil, err } sshKeySigner, err := ssh.NewSignerFromKey(privKey) if err != nil { return nil, err } preemptible := true if cfg.IsSet("PREEMPTIBLE") { preemptible = asBool(cfg.Get("PREEMPTIBLE")) } return &gceProvider{ client: client, projectID: projectID, imageProjectID: imageProjectID, cfg: cfg, ic: &gceInstanceConfig{ Preemptible: preemptible, DiskSize: diskSize, SSHKeySigner: sshKeySigner, SSHPubKey: string(ssh.MarshalAuthorizedKey(pubKey)), AutoImplode: autoImplode, StopPollSleep: stopPollSleep, StopPrePollSleep: stopPrePollSleep, SkipStopPoll: skipStopPoll, }, imageSelector: imageSelector, imageSelectorType: imageSelectorType, bootPollSleep: bootPollSleep, bootPrePollSleep: bootPrePollSleep, defaultLanguage: defaultLanguage, defaultImage: defaultImage, uploadRetries: uploadRetries, uploadRetrySleep: uploadRetrySleep, rateLimiter: rateLimiter, rateLimitMaxCalls: rateLimitMaxCalls, rateLimitDuration: rateLimitDuration, }, nil }