func runClusterMigrateDomain(args *docopt.Args) error { client, err := getClusterClient() if err != nil { shutdown.Fatal(err) } dm := &ct.DomainMigration{ Domain: args.String["<domain>"], } release, err := client.GetAppRelease("controller") if err != nil { return err } dm.OldDomain = release.Env["DEFAULT_ROUTE_DOMAIN"] if !promptYesNo(fmt.Sprintf("Migrate cluster domain from %q to %q?", dm.OldDomain, dm.Domain)) { fmt.Println("Aborted") return nil } maxDuration := 2 * time.Minute fmt.Printf("Migrating cluster domain (this can take up to %s)...\n", maxDuration) events := make(chan *ct.Event) stream, err := client.StreamEvents(controller.StreamEventsOptions{ ObjectTypes: []ct.EventType{ct.EventTypeDomainMigration}, }, events) if err != nil { return nil } defer stream.Close() if err := client.PutDomain(dm); err != nil { return err } timeout := time.After(maxDuration) for { select { case event, ok := <-events: if !ok { return stream.Err() } var e *ct.DomainMigrationEvent if err := json.Unmarshal(event.Data, &e); err != nil { return err } if e.Error != "" { fmt.Println(e.Error) } if e.DomainMigration.FinishedAt != nil { fmt.Printf("Changed cluster domain from %q to %q\n", dm.OldDomain, dm.Domain) return nil } case <-timeout: return errors.New("timed out waiting for domain migration to complete") } } }
func (s *DomainMigrationSuite) migrateDomain(t *c.C, dm *ct.DomainMigration) { debugf(t, "migrating domain from %s to %s", dm.OldDomain, dm.Domain) client := s.controllerClient(t) events := make(chan *ct.Event) stream, err := client.StreamEvents(controller.StreamEventsOptions{ ObjectTypes: []ct.EventType{ct.EventTypeDomainMigration}, }, events) t.Assert(err, c.IsNil) defer stream.Close() prevRouterRelease, err := client.GetAppRelease("router") t.Assert(err, c.IsNil) err = client.PutDomain(dm) t.Assert(err, c.IsNil) waitEvent := func(typ string, timeout time.Duration) (event ct.DomainMigrationEvent) { debugf(t, "waiting for %s domain migration event", typ) var e *ct.Event var ok bool select { case e, ok = <-events: if !ok { t.Fatal("event stream closed unexpectedly") } debugf(t, "got %s domain migration event", typ) case <-time.After(timeout): t.Fatalf("timed out waiting for %s domain migration event", typ) } t.Assert(e.Data, c.NotNil) t.Assert(json.Unmarshal(e.Data, &event), c.IsNil) return } // created event := waitEvent("initial", 2*time.Minute) t.Assert(event.Error, c.Equals, "") t.Assert(event.DomainMigration, c.NotNil) t.Assert(event.DomainMigration.ID, c.Equals, dm.ID) t.Assert(event.DomainMigration.OldDomain, c.Equals, dm.OldDomain) t.Assert(event.DomainMigration.Domain, c.Equals, dm.Domain) t.Assert(event.DomainMigration.TLSCert, c.IsNil) t.Assert(event.DomainMigration.OldTLSCert, c.NotNil) t.Assert(event.DomainMigration.CreatedAt, c.NotNil) t.Assert(event.DomainMigration.CreatedAt.Equal(*dm.CreatedAt), c.Equals, true) t.Assert(event.DomainMigration.FinishedAt, c.IsNil) // complete event = waitEvent("final", 3*time.Minute) t.Assert(event.Error, c.Equals, "") t.Assert(event.DomainMigration, c.NotNil) t.Assert(event.DomainMigration.ID, c.Equals, dm.ID) t.Assert(event.DomainMigration.OldDomain, c.Equals, dm.OldDomain) t.Assert(event.DomainMigration.Domain, c.Equals, dm.Domain) t.Assert(event.DomainMigration.TLSCert, c.NotNil) t.Assert(event.DomainMigration.OldTLSCert, c.NotNil) t.Assert(event.DomainMigration.CreatedAt, c.NotNil) t.Assert(event.DomainMigration.CreatedAt.Equal(*dm.CreatedAt), c.Equals, true) t.Assert(event.DomainMigration.FinishedAt, c.NotNil) cert := event.DomainMigration.TLSCert controllerRelease, err := client.GetAppRelease("controller") t.Assert(err, c.IsNil) t.Assert(controllerRelease.Env["DEFAULT_ROUTE_DOMAIN"], c.Equals, dm.Domain) t.Assert(controllerRelease.Env["CA_CERT"], c.Equals, cert.CACert) routerRelease, err := client.GetAppRelease("router") t.Assert(err, c.IsNil) t.Assert(routerRelease.Env["TLSCERT"], c.Equals, cert.Cert) t.Assert(routerRelease.Env["TLSKEY"], c.Not(c.Equals), "") t.Assert(routerRelease.Env["TLSKEY"], c.Not(c.Equals), prevRouterRelease.Env["TLSKEY"]) dashboardRelease, err := client.GetAppRelease("dashboard") t.Assert(err, c.IsNil) t.Assert(dashboardRelease.Env["DEFAULT_ROUTE_DOMAIN"], c.Equals, dm.Domain) t.Assert(dashboardRelease.Env["CONTROLLER_DOMAIN"], c.Equals, fmt.Sprintf("controller.%s", dm.Domain)) t.Assert(dashboardRelease.Env["URL"], c.Equals, fmt.Sprintf("dashboard.%s", dm.Domain)) t.Assert(dashboardRelease.Env["CA_CERT"], c.Equals, cert.CACert) var doPing func(string, int) doPing = func(component string, retriesRemaining int) { url := fmt.Sprintf("http://%s.%s/ping", component, dm.Domain) res, err := (&http.Client{}).Get(url) if (err != nil || res.StatusCode != 200) && retriesRemaining > 0 { time.Sleep(100 * time.Millisecond) doPing(component, retriesRemaining-1) return } t.Assert(err, c.IsNil) t.Assert(res.StatusCode, c.Equals, 200, c.Commentf("failed to ping %s", component)) } doPing("controller", 3) doPing("dashboard", 3) }
func runClusterMigrateDomain(args *docopt.Args) error { cluster, err := getCluster() if err != nil { shutdown.Fatal(err) } client, err := cluster.Client() if err != nil { shutdown.Fatal(err) } dm := &ct.DomainMigration{ Domain: args.String["<domain>"], } release, err := client.GetAppRelease("controller") if err != nil { return err } dm.OldDomain = release.Env["DEFAULT_ROUTE_DOMAIN"] if !promptYesNo(fmt.Sprintf("Migrate cluster domain from %q to %q?", dm.OldDomain, dm.Domain)) { fmt.Println("Aborted") return nil } maxDuration := 2 * time.Minute fmt.Printf("Migrating cluster domain (this can take up to %s)...\n", maxDuration) events := make(chan *ct.Event) stream, err := client.StreamEvents(ct.StreamEventsOptions{ ObjectTypes: []ct.EventType{ct.EventTypeDomainMigration}, }, events) if err != nil { return nil } defer stream.Close() if err := client.PutDomain(dm); err != nil { return err } timeout := time.After(maxDuration) for { select { case event, ok := <-events: if !ok { return stream.Err() } var e *ct.DomainMigrationEvent if err := json.Unmarshal(event.Data, &e); err != nil { return err } if e.Error != "" { fmt.Println(e.Error) } if e.DomainMigration.FinishedAt != nil { dm = e.DomainMigration fmt.Printf("Changed cluster domain from %q to %q\n", dm.OldDomain, dm.Domain) // update flynnrc cluster.TLSPin = dm.TLSCert.Pin cluster.ControllerURL = fmt.Sprintf("https://controller.%s", dm.Domain) cluster.GitURL = fmt.Sprintf("https://git.%s", dm.Domain) cluster.DockerPushURL = fmt.Sprintf("https://docker.%s", dm.Domain) if err := config.SaveTo(configPath()); err != nil { return fmt.Errorf("Error saving config: %s", err) } // update git config caFile, err := cfg.CACertFile(cluster.Name) if err != nil { return err } defer caFile.Close() if _, err := caFile.Write([]byte(dm.TLSCert.CACert)); err != nil { return err } if err := cfg.WriteGlobalGitConfig(cluster.GitURL, caFile.Name()); err != nil { return err } cfg.RemoveGlobalGitConfig(fmt.Sprintf("https://git.%s", dm.OldDomain)) // try to run "docker login" for the new domain, but just print a warning // if it fails so the user can fix it later if host, err := cluster.DockerPushHost(); err == nil { if err := dockerLogin(host, cluster.Key); err == ErrDockerTLSError { printDockerTLSWarning(host, caFile.Name()) } } dockerLogout(dm.OldDomain) fmt.Println("Updated local CLI configuration") return nil } case <-timeout: return errors.New("timed out waiting for domain migration to complete") } } }