github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/http2/h2demo/launch.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"encoding/json"
    13  	"flag"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"log"
    18  	"net/http"
    19  	"os"
    20  	"strings"
    21  	"time"
    22  
    23  	"golang.org/x/oauth2"
    24  	"golang.org/x/oauth2/google"
    25  	compute "google.golang.org/api/compute/v1"
    26  )
    27  
    28  var (
    29  	proj     = flag.String("project", "symbolic-datum-552", "name of Project")
    30  	zone     = flag.String("zone", "us-central1-a", "GCE zone")
    31  	mach     = flag.String("machinetype", "n1-standard-1", "Machine type")
    32  	instName = flag.String("instance_name", "http2-demo", "Name of VM instance.")
    33  	sshPub   = flag.String("ssh_public_key", "", "ssh public key file to authorize. Can modify later in Google's web UI anyway.")
    34  	staticIP = flag.String("static_ip", "130.211.116.44", "Static IP to use. If empty, automatic.")
    35  
    36  	writeObject  = flag.String("write_object", "", "If non-empty, a VM isn't created and the flag value is Google Cloud Storage bucket/object to write. The contents from stdin.")
    37  	publicObject = flag.Bool("write_object_is_public", false, "Whether the object created by --write_object should be public.")
    38  )
    39  
    40  func readFile(v string) string {
    41  	slurp, err := ioutil.ReadFile(v)
    42  	if err != nil {
    43  		log.Fatalf("Error reading %s: %v", v, err)
    44  	}
    45  	return strings.TrimSpace(string(slurp))
    46  }
    47  
    48  var config = &oauth2.Config{
    49  	// The client-id and secret should be for an "Installed Application" when using
    50  	// the CLI. Later we'll use a web application with a callback.
    51  	ClientID:     readFile("client-id.dat"),
    52  	ClientSecret: readFile("client-secret.dat"),
    53  	Endpoint:     google.Endpoint,
    54  	Scopes: []string{
    55  		compute.DevstorageFullControlScope,
    56  		compute.ComputeScope,
    57  		"https://www.googleapis.com/auth/sqlservice",
    58  		"https://www.googleapis.com/auth/sqlservice.admin",
    59  	},
    60  	RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
    61  }
    62  
    63  const baseConfig = `#cloud-config
    64  coreos:
    65    units:
    66      - name: h2demo.service
    67        command: start
    68        content: |
    69          [Unit]
    70          Description=HTTP2 Demo
    71          
    72          [Service]
    73          ExecStartPre=/bin/bash -c 'mkdir -p /opt/bin && curl -s -o /opt/bin/h2demo http://storage.googleapis.com/http2-demo-server-tls/h2demo && chmod +x /opt/bin/h2demo'
    74          ExecStart=/opt/bin/h2demo --prod
    75          RestartSec=5s
    76          Restart=always
    77          Type=simple
    78          
    79          [Install]
    80          WantedBy=multi-user.target
    81  `
    82  
    83  func main() {
    84  	flag.Parse()
    85  	if *proj == "" {
    86  		log.Fatalf("Missing --project flag")
    87  	}
    88  	prefix := "https://www.googleapis.com/compute/v1/projects/" + *proj
    89  	machType := prefix + "/zones/" + *zone + "/machineTypes/" + *mach
    90  
    91  	const tokenFileName = "token.dat"
    92  	tokenFile := tokenCacheFile(tokenFileName)
    93  	tokenSource := oauth2.ReuseTokenSource(nil, tokenFile)
    94  	token, err := tokenSource.Token()
    95  	if err != nil {
    96  		if *writeObject != "" {
    97  			log.Fatalf("Can't use --write_object without a valid token.dat file already cached.")
    98  		}
    99  		log.Printf("Error getting token from %s: %v", tokenFileName, err)
   100  		log.Printf("Get auth code from %v", config.AuthCodeURL("my-state"))
   101  		fmt.Print("\nEnter auth code: ")
   102  		sc := bufio.NewScanner(os.Stdin)
   103  		sc.Scan()
   104  		authCode := strings.TrimSpace(sc.Text())
   105  		token, err = config.Exchange(oauth2.NoContext, authCode)
   106  		if err != nil {
   107  			log.Fatalf("Error exchanging auth code for a token: %v", err)
   108  		}
   109  		if err := tokenFile.WriteToken(token); err != nil {
   110  			log.Fatalf("Error writing to %s: %v", tokenFileName, err)
   111  		}
   112  		tokenSource = oauth2.ReuseTokenSource(token, nil)
   113  	}
   114  
   115  	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
   116  
   117  	if *writeObject != "" {
   118  		writeCloudStorageObject(oauthClient)
   119  		return
   120  	}
   121  
   122  	computeService, _ := compute.New(oauthClient)
   123  
   124  	natIP := *staticIP
   125  	if natIP == "" {
   126  		// Try to find it by name.
   127  		aggAddrList, err := computeService.Addresses.AggregatedList(*proj).Do()
   128  		if err != nil {
   129  			log.Fatal(err)
   130  		}
   131  		// http://godoc.org/code.google.com/p/google-api-go-client/compute/v1#AddressAggregatedList
   132  	IPLoop:
   133  		for _, asl := range aggAddrList.Items {
   134  			for _, addr := range asl.Addresses {
   135  				if addr.Name == *instName+"-ip" && addr.Status == "RESERVED" {
   136  					natIP = addr.Address
   137  					break IPLoop
   138  				}
   139  			}
   140  		}
   141  	}
   142  
   143  	cloudConfig := baseConfig
   144  	if *sshPub != "" {
   145  		key := strings.TrimSpace(readFile(*sshPub))
   146  		cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n    - %s\n", key)
   147  	}
   148  	if os.Getenv("USER") == "bradfitz" {
   149  		cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n    - %s\n", "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwks9dwWKlRC+73gRbvYtVg0vdCwDSuIlyt4z6xa/YU/jTDynM4R4W10hm2tPjy8iR1k8XhDv4/qdxe6m07NjG/By1tkmGpm1mGwho4Pr5kbAAy/Qg+NLCSdAYnnE00FQEcFOC15GFVMOW2AzDGKisReohwH9eIzHPzdYQNPRWXE= bradfitz@papag.bradfitz.com")
   150  	}
   151  	const maxCloudConfig = 32 << 10 // per compute API docs
   152  	if len(cloudConfig) > maxCloudConfig {
   153  		log.Fatalf("cloud config length of %d bytes is over %d byte limit", len(cloudConfig), maxCloudConfig)
   154  	}
   155  
   156  	instance := &compute.Instance{
   157  		Name:        *instName,
   158  		Description: "Go Builder",
   159  		MachineType: machType,
   160  		Disks:       []*compute.AttachedDisk{instanceDisk(computeService)},
   161  		Tags: &compute.Tags{
   162  			Items: []string{"http-server", "https-server"},
   163  		},
   164  		Metadata: &compute.Metadata{
   165  			Items: []*compute.MetadataItems{
   166  				{
   167  					Key:   "user-data",
   168  					Value: &cloudConfig,
   169  				},
   170  			},
   171  		},
   172  		NetworkInterfaces: []*compute.NetworkInterface{
   173  			&compute.NetworkInterface{
   174  				AccessConfigs: []*compute.AccessConfig{
   175  					&compute.AccessConfig{
   176  						Type:  "ONE_TO_ONE_NAT",
   177  						Name:  "External NAT",
   178  						NatIP: natIP,
   179  					},
   180  				},
   181  				Network: prefix + "/global/networks/default",
   182  			},
   183  		},
   184  		ServiceAccounts: []*compute.ServiceAccount{
   185  			{
   186  				Email: "default",
   187  				Scopes: []string{
   188  					compute.DevstorageFullControlScope,
   189  					compute.ComputeScope,
   190  				},
   191  			},
   192  		},
   193  	}
   194  
   195  	log.Printf("Creating instance...")
   196  	op, err := computeService.Instances.Insert(*proj, *zone, instance).Do()
   197  	if err != nil {
   198  		log.Fatalf("Failed to create instance: %v", err)
   199  	}
   200  	opName := op.Name
   201  	log.Printf("Created. Waiting on operation %v", opName)
   202  OpLoop:
   203  	for {
   204  		time.Sleep(2 * time.Second)
   205  		op, err := computeService.ZoneOperations.Get(*proj, *zone, opName).Do()
   206  		if err != nil {
   207  			log.Fatalf("Failed to get op %s: %v", opName, err)
   208  		}
   209  		switch op.Status {
   210  		case "PENDING", "RUNNING":
   211  			log.Printf("Waiting on operation %v", opName)
   212  			continue
   213  		case "DONE":
   214  			if op.Error != nil {
   215  				for _, operr := range op.Error.Errors {
   216  					log.Printf("Error: %+v", operr)
   217  				}
   218  				log.Fatalf("Failed to start.")
   219  			}
   220  			log.Printf("Success. %+v", op)
   221  			break OpLoop
   222  		default:
   223  			log.Fatalf("Unknown status %q: %+v", op.Status, op)
   224  		}
   225  	}
   226  
   227  	inst, err := computeService.Instances.Get(*proj, *zone, *instName).Do()
   228  	if err != nil {
   229  		log.Fatalf("Error getting instance after creation: %v", err)
   230  	}
   231  	ij, _ := json.MarshalIndent(inst, "", "    ")
   232  	log.Printf("Instance: %s", ij)
   233  }
   234  
   235  func instanceDisk(svc *compute.Service) *compute.AttachedDisk {
   236  	const imageURL = "https://www.googleapis.com/compute/v1/projects/coreos-cloud/global/images/coreos-stable-444-5-0-v20141016"
   237  	diskName := *instName + "-disk"
   238  
   239  	return &compute.AttachedDisk{
   240  		AutoDelete: true,
   241  		Boot:       true,
   242  		Type:       "PERSISTENT",
   243  		InitializeParams: &compute.AttachedDiskInitializeParams{
   244  			DiskName:    diskName,
   245  			SourceImage: imageURL,
   246  			DiskSizeGb:  50,
   247  		},
   248  	}
   249  }
   250  
   251  func writeCloudStorageObject(httpClient *http.Client) {
   252  	content := os.Stdin
   253  	const maxSlurp = 1 << 20
   254  	var buf bytes.Buffer
   255  	n, err := io.CopyN(&buf, content, maxSlurp)
   256  	if err != nil && err != io.EOF {
   257  		log.Fatalf("Error reading from stdin: %v, %v", n, err)
   258  	}
   259  	contentType := http.DetectContentType(buf.Bytes())
   260  
   261  	req, err := http.NewRequest("PUT", "https://storage.googleapis.com/"+*writeObject, io.MultiReader(&buf, content))
   262  	if err != nil {
   263  		log.Fatal(err)
   264  	}
   265  	req.Header.Set("x-goog-api-version", "2")
   266  	if *publicObject {
   267  		req.Header.Set("x-goog-acl", "public-read")
   268  	}
   269  	req.Header.Set("Content-Type", contentType)
   270  	res, err := httpClient.Do(req)
   271  	if err != nil {
   272  		log.Fatal(err)
   273  	}
   274  	if res.StatusCode != 200 {
   275  		res.Write(os.Stderr)
   276  		log.Fatalf("Failed.")
   277  	}
   278  	log.Printf("Success.")
   279  	os.Exit(0)
   280  }
   281  
   282  type tokenCacheFile string
   283  
   284  func (f tokenCacheFile) Token() (*oauth2.Token, error) {
   285  	slurp, err := ioutil.ReadFile(string(f))
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	t := new(oauth2.Token)
   290  	if err := json.Unmarshal(slurp, t); err != nil {
   291  		return nil, err
   292  	}
   293  	return t, nil
   294  }
   295  
   296  func (f tokenCacheFile) WriteToken(t *oauth2.Token) error {
   297  	jt, err := json.Marshal(t)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	return ioutil.WriteFile(string(f), jt, 0600)
   302  }