Exemple #1
0
func (a *Api) Run() error {
	globalMux := http.NewServeMux()
	controllerManager := a.manager
	client := a.manager.DockerClient()

	// forwarder for swarm
	var err error
	a.fwd, err = forward.New()
	if err != nil {
		return err
	}

	u := client.URL

	// setup redirect target to swarm
	scheme := "http://"

	// check if TLS is enabled and configure if so
	if client.TLSConfig != nil {
		log.Debug("configuring ssl for swarm redirect")
		scheme = "https://"
		// setup custom roundtripper with TLS transport
		r := forward.RoundTripper(
			&http.Transport{
				TLSClientConfig: client.TLSConfig,
			})
		f, err := forward.New(r)
		if err != nil {
			return err
		}

		a.fwd = f
	}

	a.dUrl = fmt.Sprintf("%s%s", scheme, u.Host)

	log.Debugf("configured docker proxy target: %s", a.dUrl)

	swarmRedirect := http.HandlerFunc(a.swarmRedirect)

	swarmHijack := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		a.swarmHijack(client.TLSConfig, a.dUrl, w, req)
	})

	apiRouter := mux.NewRouter()
	apiRouter.HandleFunc("/api/accounts", a.accounts).Methods("GET")
	apiRouter.HandleFunc("/api/accounts", a.saveAccount).Methods("POST")
	apiRouter.HandleFunc("/api/accounts/{username}", a.account).Methods("GET")
	apiRouter.HandleFunc("/api/accounts/{username}", a.deleteAccount).Methods("DELETE")
	apiRouter.HandleFunc("/api/svcregs", a.serviceRegs).Methods("GET")
	apiRouter.HandleFunc("/api/svcregs", a.saveServiceReg).Methods("POST")
	apiRouter.HandleFunc("/api/svcregs/{service_name}", a.serviceReg).Methods("GET")
	apiRouter.HandleFunc("/api/svcregs/{service_name}", a.deleteServiceReg).Methods("DELETE")
	apiRouter.HandleFunc("/api/roles", a.roles).Methods("GET")
	apiRouter.HandleFunc("/api/roles/{name}", a.role).Methods("GET")
	apiRouter.HandleFunc("/api/nodes", a.nodes).Methods("GET")
	apiRouter.HandleFunc("/api/nodes/{name}", a.node).Methods("GET")
	apiRouter.HandleFunc("/api/containers/{id}/scale", a.scaleContainer).Methods("POST")
	apiRouter.HandleFunc("/api/events", a.events).Methods("GET")
	apiRouter.HandleFunc("/api/events", a.purgeEvents).Methods("DELETE")
	apiRouter.HandleFunc("/api/registries", a.registries).Methods("GET")
	apiRouter.HandleFunc("/api/registries", a.addRegistry).Methods("POST")
	apiRouter.HandleFunc("/api/registries/{name}", a.registry).Methods("GET")
	apiRouter.HandleFunc("/api/registries/{name}", a.removeRegistry).Methods("DELETE")
	apiRouter.HandleFunc("/api/registries/{name}/repositories", a.repositories).Methods("GET")
	apiRouter.HandleFunc("/api/registries/{name}/repositories/{repo:.*}", a.repository).Methods("GET")
	apiRouter.HandleFunc("/api/registries/{name}/repositories/{repo:.*}", a.deleteRepository).Methods("DELETE")
	apiRouter.HandleFunc("/api/servicekeys", a.serviceKeys).Methods("GET")
	apiRouter.HandleFunc("/api/servicekeys", a.addServiceKey).Methods("POST")
	apiRouter.HandleFunc("/api/servicekeys", a.removeServiceKey).Methods("DELETE")
	apiRouter.HandleFunc("/api/webhookkeys", a.webhookKeys).Methods("GET")
	apiRouter.HandleFunc("/api/webhookkeys/{id}", a.webhookKey).Methods("GET")
	apiRouter.HandleFunc("/api/webhookkeys", a.addWebhookKey).Methods("POST")
	apiRouter.HandleFunc("/api/webhookkeys/{id}", a.deleteWebhookKey).Methods("DELETE")
	apiRouter.HandleFunc("/api/consolesession/{container}", a.createConsoleSession).Methods("GET")
	apiRouter.HandleFunc("/api/consolesession/{token}", a.consoleSession).Methods("GET")
	apiRouter.HandleFunc("/api/consolesession/{token}", a.removeConsoleSession).Methods("DELETE")
	apiRouter.HandleFunc("/api/getcloudaddr", a.getCloudAddr).Methods("GET")

	// global handler
	globalMux.Handle("/", http.FileServer(http.Dir("static")))

	auditExcludes := []string{
		"^/containers/json",
		"^/images/json",
		"^/api/events",
	}
	apiAuditor := audit.NewAuditor(controllerManager, auditExcludes)

	// api router; protected by auth
	apiAuthRouter := negroni.New()
	//	apiAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs)
	//	apiAccessRequired := access.NewAccessRequired(controllerManager)
	//	apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext))
	//	apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext))
	apiAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext))
	apiAuthRouter.UseHandler(apiRouter)
	globalMux.Handle("/api/", apiAuthRouter)

	// account router ; protected by auth
	accountRouter := mux.NewRouter()
	accountRouter.HandleFunc("/account/changepassword", a.changePassword).Methods("POST")
	accountAuthRouter := negroni.New()
	accountAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs)
	accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext))
	accountAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext))
	accountAuthRouter.UseHandler(accountRouter)
	globalMux.Handle("/account/", accountAuthRouter)

	// login handler; public
	loginRouter := mux.NewRouter()
	loginRouter.HandleFunc("/auth/login", a.login).Methods("POST")
	globalMux.Handle("/auth/", loginRouter)
	globalMux.Handle("/exec", websocket.Handler(a.execContainer))

	// hub handler; public
	hubRouter := mux.NewRouter()
	hubRouter.HandleFunc("/hub/webhook/{id}", a.hubWebhook).Methods("POST")
	globalMux.Handle("/hub/", hubRouter)

	// swarm
	swarmRouter := mux.NewRouter()
	// these are pulled from the swarm api code to proxy and allow
	// usage with the standard Docker cli
	m := map[string]map[string]http.HandlerFunc{
		"GET": {
			"/_ping":                          swarmRedirect,
			"/events":                         swarmRedirect,
			"/info":                           swarmRedirect,
			"/version":                        swarmRedirect,
			"/images/json":                    swarmRedirect,
			"/images/viz":                     swarmRedirect,
			"/images/search":                  swarmRedirect,
			"/images/get":                     swarmRedirect,
			"/images/{name:.*}/get":           swarmRedirect,
			"/images/{name:.*}/history":       swarmRedirect,
			"/images/{name:.*}/json":          swarmRedirect,
			"/containers/ps":                  swarmRedirect,
			"/containers/json":                swarmRedirect,
			"/containers/{name:.*}/export":    swarmRedirect,
			"/containers/{name:.*}/changes":   swarmRedirect,
			"/containers/{name:.*}/json":      swarmRedirect,
			"/containers/{name:.*}/top":       swarmRedirect,
			"/containers/{name:.*}/logs":      swarmRedirect,
			"/containers/{name:.*}/stats":     swarmRedirect,
			"/containers/{name:.*}/attach/ws": swarmHijack,
			"/exec/{execid:.*}/json":          swarmRedirect,
		},
		"POST": {
			"/auth":                         swarmRedirect,
			"/commit":                       swarmRedirect,
			"/build":                        swarmRedirect,
			"/images/create":                swarmRedirect,
			"/images/load":                  swarmRedirect,
			"/images/{name:.*}/push":        swarmRedirect,
			"/images/{name:.*}/tag":         swarmRedirect,
			"/containers/create":            swarmRedirect,
			"/containers/{name:.*}/kill":    swarmRedirect,
			"/containers/{name:.*}/pause":   swarmRedirect,
			"/containers/{name:.*}/unpause": swarmRedirect,
			"/containers/{name:.*}/rename":  swarmRedirect,
			"/containers/{name:.*}/restart": swarmRedirect,
			"/containers/{name:.*}/start":   swarmRedirect,
			"/containers/{name:.*}/stop":    swarmRedirect,
			"/containers/{name:.*}/wait":    swarmRedirect,
			"/containers/{name:.*}/resize":  swarmRedirect,
			"/containers/{name:.*}/attach":  swarmHijack,
			"/containers/{name:.*}/copy":    swarmRedirect,
			"/containers/{name:.*}/exec":    swarmRedirect,
			"/exec/{execid:.*}/start":       swarmHijack,
			"/exec/{execid:.*}/resize":      swarmRedirect,
		},
		"DELETE": {
			"/containers/{name:.*}": swarmRedirect,
			"/images/{name:.*}":     swarmRedirect,
		},
		"OPTIONS": {
			"": swarmRedirect,
		},
	}

	for method, routes := range m {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				if a.enableCors {
					writeCorsHeaders(w, r)
				}
				localFct(w, r)
			}
			localMethod := method

			// add the new route
			swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
			swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	swarmAuthRouter := negroni.New()
	//	swarmAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs)
	//	swarmAccessRequired := access.NewAccessRequired(controllerManager)
	//	swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext))
	//	swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext))
	//	swarmAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext))
	swarmAuthRouter.UseHandler(swarmRouter)
	globalMux.Handle("/containers/", swarmAuthRouter)
	globalMux.Handle("/_ping", swarmAuthRouter)
	globalMux.Handle("/commit", swarmAuthRouter)
	globalMux.Handle("/build", swarmAuthRouter)
	globalMux.Handle("/events", swarmAuthRouter)
	globalMux.Handle("/version", swarmAuthRouter)
	globalMux.Handle("/images/", swarmAuthRouter)
	globalMux.Handle("/exec/", swarmAuthRouter)
	globalMux.Handle("/v1.14/", swarmAuthRouter)
	globalMux.Handle("/v1.15/", swarmAuthRouter)
	globalMux.Handle("/v1.16/", swarmAuthRouter)
	globalMux.Handle("/v1.17/", swarmAuthRouter)
	globalMux.Handle("/v1.18/", swarmAuthRouter)
	globalMux.Handle("/v1.19/", swarmAuthRouter)
	globalMux.Handle("/v1.20/", swarmAuthRouter)

	//eureka

	eurekaRouter := mux.NewRouter()

	a.eUrl = fmt.Sprintf("%s%s", scheme, a.eurekaAddr)

	log.Debugf("configured eureka proxy target: %s", a.eUrl)

	eurekaRedirect := http.HandlerFunc(a.eurekaRedirect)

	n := map[string]map[string]http.HandlerFunc{
		"GET": {
			"/eureka/apps": eurekaRedirect,
		},
	}

	for method, routes := range n {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				if a.enableCors {
					writeCorsHeaders(w, r)
				}
				localFct(w, r)
			}
			localMethod := method

			// add the new route
			eurekaRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	eurekaAuthRouter := negroni.New()
	//	eurekaAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs)
	//	eurekaAccessRequired := access.NewAccessRequired(controllerManager)
	//	eurekaAuthRouter.Use(negroni.HandlerFunc(eurekaAuthRequired.HandlerFuncWithNext))
	//	eurekaAuthRouter.Use(negroni.HandlerFunc(eurekaAccessRequired.HandlerFuncWithNext))
	eurekaAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext))
	eurekaAuthRouter.UseHandler(eurekaRouter)

	globalMux.Handle("/eureka/", eurekaAuthRouter)

	//hystrix

	hystrixRouter := mux.NewRouter()

	a.hUrl = fmt.Sprintf("%s%s", scheme, a.hystrixAddr)

	log.Debugf("configured hystrix proxy target: %s", a.hUrl)

	hystrixRedirect := http.HandlerFunc(a.hystrixRedirect)

	o := map[string]map[string]http.HandlerFunc{
		"GET": {
			"/proxy.stream": hystrixRedirect,
		},
	}

	for method, routes := range o {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				if a.enableCors {
					writeCorsHeaders(w, r)
				}
				localFct(w, r)
			}
			localMethod := method

			// add the new route
			hystrixRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	hystrixAuthRouter := negroni.New()
	//	hystrixAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs)
	//	hystrixAccessRequired := access.NewAccessRequired(controllerManager)
	//	hystrixAuthRouter.Use(negroni.HandlerFunc(hystrixAuthRequired.HandlerFuncWithNext))
	//	hystrixAuthRouter.Use(negroni.HandlerFunc(hystrixAccessRequired.HandlerFuncWithNext))
	//	hystrixAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext))
	hystrixAuthRouter.UseHandler(hystrixRouter)

	globalMux.Handle("/proxy.stream", hystrixAuthRouter)

	// check for admin user
	if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist {
		// create roles
		acct := &auth.Account{
			Username:  "******",
			Password:  "******",
			FirstName: "Shipyard",
			LastName:  "Admin",
			Roles:     []string{"admin"},
		}
		if err := controllerManager.SaveAccount(acct); err != nil {
			log.Fatal(err)
		}
		log.Infof("created admin user: username: admin password: shipyard")
	}

	log.Infof("controller listening on %s", a.listenAddr)

	s := &http.Server{
		Addr:    a.listenAddr,
		Handler: context.ClearHandler(globalMux),
	}

	var runErr error

	if a.tlsCertPath != "" && a.tlsKeyPath != "" {
		log.Infof("using TLS for communication: cert=%s key=%s",
			a.tlsCertPath,
			a.tlsKeyPath,
		)

		// setup TLS config
		var caCert []byte
		if a.tlsCACertPath != "" {
			ca, err := ioutil.ReadFile(a.tlsCACertPath)
			if err != nil {
				return err
			}

			caCert = ca
		}

		serverCert, err := ioutil.ReadFile(a.tlsCertPath)
		if err != nil {
			return err
		}

		serverKey, err := ioutil.ReadFile(a.tlsKeyPath)
		if err != nil {
			return err
		}

		tlsConfig, err := tlsutils.GetServerTLSConfig(caCert, serverCert, serverKey, a.allowInsecure)
		if err != nil {
			return err
		}

		s.TLSConfig = tlsConfig

		runErr = s.ListenAndServeTLS(a.tlsCertPath, a.tlsKeyPath)
	} else {
		runErr = s.ListenAndServe()
	}

	return runErr
}
Exemple #2
0
func (a *Api) Run() error {
	globalMux := http.NewServeMux()

	// forwarder for swarm
	var err error
	a.fwd, err = forward.New()
	if err != nil {
		return err
	}

	u := a.client.URL
	// setup redirect target to swarm
	scheme := "http://"

	// check if TLS is enabled and configure if so
	if a.client.TLSConfig != nil {
		log.Debug("configuring ssl for swarm redirect")
		scheme = "https://"
		// setup custom roundtripper with TLS transport
		r := forward.RoundTripper(
			&http.Transport{
				TLSClientConfig: a.client.TLSConfig,
			})
		f, err := forward.New(r)
		if err != nil {
			return err
		}

		a.fwd = f
	}

	// init key-value path
	if err := a.initPath(); err != nil {
		return err
	}

	a.dUrl = fmt.Sprintf("%s%s", scheme, u.Host)

	log.Debugf("configured docker proxy target: %s", a.dUrl)

	swarmRedirect := http.HandlerFunc(a.swarmRedirect)

	swarmHijack := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		a.swarmHijack(a.client.TLSConfig, a.dUrl, w, req)
	})

	mh := map[string]map[string]http.HandlerFunc{
		"GET": {
			"/api/gateways":                a.gateways,
			"/api/gateways/{id}":           a.gateway,
			"/api/groups":                  a.groups,
			"/api/groups/{name}":           a.group,
			"/api/policy":                  a.policys,
			"/api/policy/{peer}":           a.policy,
			"/api/firewalls":               a.firewalls,
			"/api/firewalls/{name}":        a.firewallByContainer,
			"/api/firewalls/{node}/{port}": a.firewall,
			"/api/containers/{id}":         a.showContainer,
		},
		"POST": {
			"/api/groups":        a.saveGroup,
			"/api/groups/{name}": a.saveMember,
			"/api/policy/{peer}": a.savePolicy,
			"/api/firewalls":     a.saveFirewall,
		},
		"DELETE": {
			"/api/groups/{name}":          a.deleteGroup,
			"/api/groups/{name}/{member}": a.deleteMember,
			"/api/policy/{peer}":          a.deletePolicy,
			"/api/firewalls/{name}":       a.deleteFirewall,
		},
		"PUT": {
			"/api/containers/{id}/reset": a.resetContainer,
		},
	}

	apiRouter := mux.NewRouter()
	for method, routes := range mh {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				localFct(w, r)
			}
			localMethod := method

			// add the new route
			apiRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
			apiRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	// global handler
	globalMux.Handle("/", http.FileServer(http.Dir("static")))
	globalMux.Handle("/api/", apiRouter)
	//globalMux.Handle("/v{version:[0-9.]+}/api/", apiRouter)
	globalMux.Handle("/v1.14/api/", apiRouter)
	globalMux.Handle("/v1.15/api/", apiRouter)
	globalMux.Handle("/v1.16/api/", apiRouter)
	globalMux.Handle("/v1.17/api/", apiRouter)
	globalMux.Handle("/v1.18/api/", apiRouter)
	globalMux.Handle("/v1.19/api/", apiRouter)
	globalMux.Handle("/v1.20/api/", apiRouter)
	globalMux.Handle("/v1.21/api/", apiRouter)
	globalMux.Handle("/v1.22/api/", apiRouter)
	globalMux.Handle("/v1.23/api/", apiRouter)
	globalMux.Handle("/v1.24/api/", apiRouter)
	globalMux.Handle("/v1.25/api/", apiRouter)
	globalMux.Handle("/v1.26/api/", apiRouter)

	// swarm
	swarmRouter := mux.NewRouter()
	// these are pulled from the swarm api code to proxy and allow
	// usage with the standard Docker cli
	m := map[string]map[string]http.HandlerFunc{
		"GET": {
			"/_ping":                          swarmRedirect,
			"/events":                         swarmRedirect,
			"/info":                           swarmRedirect,
			"/version":                        swarmRedirect,
			"/images/json":                    swarmRedirect,
			"/images/viz":                     swarmRedirect,
			"/images/search":                  swarmRedirect,
			"/images/get":                     swarmRedirect,
			"/images/{name:.*}/get":           swarmRedirect,
			"/images/{name:.*}/history":       swarmRedirect,
			"/images/{name:.*}/json":          swarmRedirect,
			"/containers/ps":                  swarmRedirect,
			"/containers/json":                swarmRedirect,
			"/containers/{name:.*}/export":    swarmRedirect,
			"/containers/{name:.*}/changes":   swarmRedirect,
			"/containers/{name:.*}/json":      swarmRedirect,
			"/containers/{name:.*}/top":       swarmRedirect,
			"/containers/{name:.*}/logs":      swarmRedirect,
			"/containers/{name:.*}/stats":     swarmRedirect,
			"/containers/{name:.*}/attach/ws": swarmHijack,
			"/exec/{execid:.*}/json":          swarmRedirect,
			"/networks":                       swarmRedirect,
			"/networks/{networkid:.*}":        swarmRedirect,
			"/volumes":                        swarmRedirect,
			"/volumes/{volumename:.*}":        swarmRedirect,
		},
		"POST": {
			"/auth":                               swarmRedirect,
			"/commit":                             swarmRedirect,
			"/build":                              swarmRedirect,
			"/images/create":                      swarmRedirect,
			"/images/load":                        swarmRedirect,
			"/images/{name:.*}/push":              swarmRedirect,
			"/images/{name:.*}/tag":               swarmRedirect,
			"/containers/create":                  swarmRedirect,
			"/containers/{name:.*}/kill":          swarmRedirect,
			"/containers/{name:.*}/pause":         swarmRedirect,
			"/containers/{name:.*}/unpause":       swarmRedirect,
			"/containers/{name:.*}/rename":        swarmRedirect,
			"/containers/{name:.*}/restart":       swarmRedirect,
			"/containers/{name:.*}/start":         swarmRedirect,
			"/containers/{name:.*}/stop":          swarmRedirect,
			"/containers/{name:.*}/wait":          swarmRedirect,
			"/containers/{name:.*}/resize":        swarmRedirect,
			"/containers/{name:.*}/attach":        swarmHijack,
			"/containers/{name:.*}/copy":          swarmRedirect,
			"/containers/{name:.*}/exec":          swarmRedirect,
			"/exec/{execid:.*}/start":             swarmHijack,
			"/exec/{execid:.*}/resize":            swarmRedirect,
			"/networks/create":                    swarmRedirect,
			"/networks/{networkid:.*}/connect":    swarmRedirect,
			"/networks/{networkid:.*}/disconnect": swarmRedirect,
			"/volumes/create":                     swarmRedirect,
		},
		"PUT": {
			"/containers/{name:.*}/archive": swarmRedirect,
		},
		"DELETE": {
			"/containers/{name:.*}":    swarmRedirect,
			"/images/{name:.*}":        swarmRedirect,
			"/networks/{networkid:.*}": swarmRedirect,
			"/volumes/{name:.*}":       swarmRedirect,
		},
		"OPTIONS": {
			"": swarmRedirect,
		},
	}

	for method, routes := range m {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				localFct(w, r)
			}
			localMethod := method

			// add the new route
			swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
			swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	globalMux.Handle("/containers/", swarmRouter)
	globalMux.Handle("/_ping", swarmRouter)
	globalMux.Handle("/commit", swarmRouter)
	globalMux.Handle("/build", swarmRouter)
	globalMux.Handle("/events", swarmRouter)
	globalMux.Handle("/version", swarmRouter)
	globalMux.Handle("/images/", swarmRouter)
	globalMux.Handle("/exec/", swarmRouter)
	globalMux.Handle("/v1.14/", swarmRouter)
	globalMux.Handle("/v1.15/", swarmRouter)
	globalMux.Handle("/v1.16/", swarmRouter)
	globalMux.Handle("/v1.17/", swarmRouter)
	globalMux.Handle("/v1.18/", swarmRouter)
	globalMux.Handle("/v1.19/", swarmRouter)
	globalMux.Handle("/v1.20/", swarmRouter)
	globalMux.Handle("/v1.21/", swarmRouter)
	globalMux.Handle("/v1.22/", swarmRouter)
	globalMux.Handle("/v1.23/", swarmRouter)
	globalMux.Handle("/v1.24/", swarmRouter)
	globalMux.Handle("/v1.25/", swarmRouter)
	globalMux.Handle("/v1.26/", swarmRouter)

	log.Infof("controller listening on %s", a.listenAddr)

	s := &http.Server{
		Addr:    a.listenAddr,
		Handler: context.ClearHandler(globalMux),
	}

	var runErr error
	runErr = s.ListenAndServe()
	return runErr
}