diff --git a/.gitignore b/.gitignore index 2dcc738b5e..273161d4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,11 @@ lib/config/dynamic/admiral/client lib/config/dynamic/admiral/models lib/config/dynamic/admiral/operations +lib/apiservers/service/models +lib/apiservers/service/restapi/*.go +lib/apiservers/service/restapi/operations +!lib/apiservers/service/restapi/configure_port_layer.go + tests/.project # go test binaries *.test diff --git a/Makefile b/Makefile index eb7cb2481d..47eaa90065 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,8 @@ portlayerapi := $(BIN)/port-layer-server portlayerapi-test := $(BIN)/port-layer-server-test portlayerapi-client := lib/apiservers/portlayer/client/port_layer_client.go portlayerapi-server := lib/apiservers/portlayer/restapi/server.go +serviceapi := $(BIN)/vic-machine-server +serviceapi-server := lib/apiservers/service/restapi/server.go imagec := $(BIN)/imagec vicadmin := $(BIN)/vicadmin @@ -123,6 +125,8 @@ portlayerapi: $(portlayerapi) portlayerapi-test: $(portlayerapi-test) portlayerapi-client: $(portlayerapi-client) portlayerapi-server: $(portlayerapi-server) +serviceapi: $(serviceapi) +serviceapi-server: $(serviceapi-server) admiralapi-client: $(admiralapi-client) imagec: $(imagec) @@ -156,12 +160,12 @@ misspell: $(MISSPELL) all: components tethers isos vic-machine imagec vic-ui tools: $(GOIMPORTS) $(GVT) $(GOLINT) $(SWAGGER) $(GAS) $(MISSPELL) goversion check: goversion goimports gofmt misspell govet golint copyright whitespace gas -apiservers: $(portlayerapi) $(docker-engine-api) +apiservers: $(portlayerapi) $(docker-engine-api) $(serviceapi) components: check apiservers $(vicadmin) $(rpctool) isos: $(appliance) $(bootstrap) tethers: $(tether-linux) -most: $(portlayerapi) $(docker-engine-api) $(vicadmin) $(tether-linux) $(appliance) $(bootstrap) $(vic-machine-linux) +most: $(portlayerapi) $(docker-engine-api) $(vicadmin) $(tether-linux) $(appliance) $(bootstrap) $(vic-machine-linux) $(serviceapi-server) # utility targets goversion: @@ -355,6 +359,24 @@ $(portlayerapi-test): $$(call godeps,cmd/port-layer-server/*.go) $(portlayerapi- @echo building Portlayer API server for test... @$(TIME) $(GO) test -c -coverpkg github.com/vmware/vic/lib/...,github.com/vmware/vic/pkg/... -coverprofile port-layer-server.cov -outputdir /tmp -o $@ ./cmd/port-layer-server +# Common service dependencies between client and server +SERVICE_DEPS ?= lib/apiservers/service/swagger.json #\ +# lib/apiservers/service/restapi/configure_service.go \ +# lib/apiservers/service/restapi/options/*.go \ +# lib/apiservers/service/restapi/handlers/*.go + + + +$(serviceapi-server): $(SERVICE_DEPS) $(SWAGGER) + @echo regenerating swagger models and operations for vic-machine-as-a-service API server... + @$(SWAGGER) generate server --exclude-main --target lib/apiservers/service -f lib/apiservers/service/swagger.json 2>>service-swagger-gen.log + @echo done regenerating swagger models and operations for vic-machine-as-a-service API server... + +$(serviceapi): $$(call godeps,cmd/vic-machine-server/*.go) $(serviceapi-server) + @echo building vic-machine-as-a-service API server... + @$(TIME) $(GO) build $(RACE) -ldflags "$(LDFLAGS)" -o $@ ./cmd/vic-machine-server + + $(iso-base): isos/base.sh isos/base/*.repo isos/base/isolinux/** isos/base/xorriso-options.cfg @echo building iso-base docker image @$(TIME) $< -c $(BIN)/.yum-cache.tgz -p $@ @@ -365,7 +387,7 @@ $(appliance-staging): isos/appliance-staging.sh $(iso-base) @$(TIME) $< -c $(BIN)/.yum-cache.tgz -p $(iso-base) -o $@ # main appliance target - depends on all top level component targets -$(appliance): isos/appliance.sh isos/appliance/* isos/vicadmin/** $(vicadmin) $(vic-init) $(portlayerapi) $(docker-engine-api) $(appliance-staging) +$(appliance): isos/appliance.sh isos/appliance/* isos/vicadmin/** $(vicadmin) $(vic-init) $(portlayerapi) $(serviceapi-server) $(docker-engine-api) $(appliance-staging) @echo building VCH appliance ISO @$(TIME) $< -p $(appliance-staging) -b $(BIN) @@ -485,6 +507,11 @@ clean: @rm -rf ./lib/config/dynamic/admiral/models @rm -rf ./lib/config/dynamic/admiral/operations + @rm -f ./lib/apiservers/service/restapi/doc.go + @rm -f ./lib/apiservers/service/restapi/embedded_spec.go + @rm -f ./lib/apiservers/service/restapi/server.go + @rm -rf ./lib/apiservers/service/restapi/operations/ + @rm -f *.log @rm -f *.pem diff --git a/cmd/vic-machine-server/main.go b/cmd/vic-machine-server/main.go new file mode 100644 index 0000000000..af19d4e36d --- /dev/null +++ b/cmd/vic-machine-server/main.go @@ -0,0 +1,70 @@ +// Copyright 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "log" + "os" + + loads "github.com/go-openapi/loads" + flags "github.com/jessevdk/go-flags" + + "github.com/vmware/vic/lib/apiservers/service/restapi" + "github.com/vmware/vic/lib/apiservers/service/restapi/operations" +) + +// This file was generated by the swagger tool. +// Make sure not to overwrite this file after you generated it because all your edits would be lost! + +func main() { + + swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "") + if err != nil { + log.Fatalln(err) + } + + api := operations.NewVicMachineAPI(swaggerSpec) + server := restapi.NewServer(api) + defer server.Shutdown() + + parser := flags.NewParser(server, flags.Default) + parser.ShortDescription = "vic-machine API" + parser.LongDescription = "An API for interacting with vic-machine as a RESTful web service." + + server.ConfigureFlags() + for _, optsGroup := range api.CommandLineOptionsGroups { + _, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options) + if err != nil { + log.Fatalln(err) + } + } + + if _, err := parser.Parse(); err != nil { + code := 1 + if fe, ok := err.(*flags.Error); ok { + if fe.Type == flags.ErrHelp { + code = 0 + } + } + os.Exit(code) + } + + server.ConfigureAPI() + + if err := server.Serve(); err != nil { + log.Fatalln(err) + } + +} diff --git a/lib/apiservers/service/restapi/configure_vic_machine.go b/lib/apiservers/service/restapi/configure_vic_machine.go new file mode 100644 index 0000000000..3513e06ca7 --- /dev/null +++ b/lib/apiservers/service/restapi/configure_vic_machine.go @@ -0,0 +1,175 @@ +// Copyright 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restapi + +import ( + "crypto/tls" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/tylerb/graceful" + + "github.com/vmware/vic/lib/apiservers/service/restapi/operations" +) + +// This file is safe to edit. Once it exists it will not be overwritten + +//go:generate swagger generate server --target ../lib/apiservers/service --name --spec ../lib/apiservers/service/swagger.json --exclude-main + +func configureFlags(api *operations.VicMachineAPI) { + // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... } +} + +func configureAPI(api *operations.VicMachineAPI) http.Handler { + // configure the api here + api.ServeError = errors.ServeError + + // Set your custom logger if needed. Default one is log.Printf + // Expected interface func(string, ...interface{}) + // + // Example: + // s.api.Logger = log.Printf + + api.JSONConsumer = runtime.JSONConsumer() + + api.JSONProducer = runtime.JSONProducer() + + api.TxtProducer = runtime.TextProducer() + + // Applies when the Authorization header is set with the Basic scheme + api.BasicAuth = func(user string, pass string) (interface{}, error) { + return nil, errors.NotImplemented("basic auth (basic) has not yet been implemented") + } + + // GET /container + api.GetHandler = operations.GetHandlerFunc(func(params operations.GetParams) middleware.Responder { + return middleware.NotImplemented("operation .Get has not yet been implemented") + }) + + // GET /container/version + api.GetVersionHandler = operations.GetVersionHandlerFunc(func(params operations.GetVersionParams) middleware.Responder { + return middleware.NotImplemented("operation .GetVersion has not yet been implemented") + }) + + // POST /container/target/{target} + api.PostTargetTargetHandler = operations.PostTargetTargetHandlerFunc(func(params operations.PostTargetTargetParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTarget has not yet been implemented") + }) + + // GET /container/target/{target}/vch + api.GetTargetTargetVchHandler = operations.GetTargetTargetVchHandlerFunc(func(params operations.GetTargetTargetVchParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .GetTargetTargetVch has not yet been implemented") + }) + + // POST /container/target/{target}/vch + api.PostTargetTargetVchHandler = operations.PostTargetTargetVchHandlerFunc(func(params operations.PostTargetTargetVchParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTargetVch has not yet been implemented") + }) + + // GET /container/target/{target}/vch/{vch-id} + api.GetTargetTargetVchVchIDHandler = operations.GetTargetTargetVchVchIDHandlerFunc(func(params operations.GetTargetTargetVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .GetTargetTargetVchVchID has not yet been implemented") + }) + + // PUT /container/target/{target}/vch/{vch-id} + api.PutTargetTargetVchVchIDHandler = operations.PutTargetTargetVchVchIDHandlerFunc(func(params operations.PutTargetTargetVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PutTargetTargetVchVchID has not yet been implemented") + }) + + // PATCH /container/target/{target}/vch/{vch-id} + api.PatchTargetTargetVchVchIDHandler = operations.PatchTargetTargetVchVchIDHandlerFunc(func(params operations.PatchTargetTargetVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PatchTargetTargetVchVchID has not yet been implemented") + }) + + // POST /container/target/{target}/vch/{vch-id} + api.PostTargetTargetVchVchIDHandler = operations.PostTargetTargetVchVchIDHandlerFunc(func(params operations.PostTargetTargetVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTargetVchVchID has not yet been implemented") + }) + + // DELETE /container/target/{target}/vch/{vch-id} + api.DeleteTargetTargetVchVchIDHandler = operations.DeleteTargetTargetVchVchIDHandlerFunc(func(params operations.DeleteTargetTargetVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .DeleteTargetTargetVchVchID has not yet been implemented") + }) + + // POST /container/target/{target}/datacenter/{datacenter} + api.PostTargetTargetDatacenterDatacenterHandler = operations.PostTargetTargetDatacenterDatacenterHandlerFunc(func(params operations.PostTargetTargetDatacenterDatacenterParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTargetDatacenterDatacenter has not yet been implemented") + }) + + // GET /container/target/{target}/datacenter/{datacenter}/vch + api.GetTargetTargetDatacenterDatacenterVchHandler = operations.GetTargetTargetDatacenterDatacenterVchHandler(func(params operations.GetTargetTargetDatacenterDatacenterVchHandler, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .GetTargetTargetDatacenterDatacenterVchHandler has not yet been implemented") + }) + + // POST /container/target/{target}/datacenter/{datacenter}/vch + api.PostTargetTargetDatacenterDatacenterVchHandler = operations.PostTargetTargetDatacenterDatacenterVchHandlerFunc(func(params operations.PostTargetTargetDatacenterDatacenterVchParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTargetDatacenterDatacenterVch has not yet been implemented") + }) + + // GET /container/target/{target}/datacenter/{datacenter}/vch/{vch-id} + api.GetTargetTargetDatacenterDatacenterVchVchIDHandler = operations.GetTargetTargetDatacenterDatacenterVchVchIDHandlerFunc(func(params operations.GetTargetTargetDatacenterDatacenterVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .GetTargetTargetDatacenterDatacenterVchVchID has not yet been implemented") + }) + + // PUT /container/target/{target}/datacenter/{datacenter}/vch/{vch-id} + api.PutTargetTargetDatacenterDatacenterVchVchIDHandler = operations.PutTargetTargetDatacenterDatacenterVchVchIDHandlerFunc(func(params operations.PutTargetTargetDatacenterDatacenterVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PutTargetTargetDatacenterDatacenterVchVchID has not yet been implemented") + }) + + // PATCH /container/target/{target}/datacenter/{datacenter}/vch/{vch-id} + api.PatchTargetTargetDatacenterDatacenterVchVchIDHandler = operations.PatchTargetTargetDatacenterDatacenterVchVchIDHandlerFunc(func(params operations.PatchTargetTargetDatacenterDatacenterVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PatchTargetTargetDatacenterDatacenterVchVchID has not yet been implemented") + }) + + // POST /container/target/{target}/datacenter/{datacenter}/vch/{vch-id} + api.PostTargetTargetDatacenterDatacenterVchVchIDHandler = operations.PostTargetTargetDatacenterDatacenterVchVchIDHandlerFunc(func(params operations.PostTargetTargetDatacenterDatacenterVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .PostTargetTargetDatacenterDatacenterVchVchID has not yet been implemented") + }) + + // DELETE /container/target/{target}/datacenter/{datacenter}/vch/{vch-id} + api.DeleteTargetTargetDatacenterDatacenterVchVchIDHandler = operations.DeleteTargetTargetDatacenterDatacenterVchVchIDHandlerFunc(func(params operations.DeleteTargetTargetDatacenterDatacenterVchVchIDParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation .DeleteTargetTargetDatacenterDatacenterVchVchID has not yet been implemented") + }) + + api.ServerShutdown = func() {} + + return setupGlobalMiddleware(api.Serve(setupMiddlewares)) +} + +// The TLS configuration before HTTPS server starts. +func configureTLS(tlsConfig *tls.Config) { + // Make all necessary changes to the TLS configuration here. +} + +// As soon as server is initialized but not run yet, this function will be called. +// If you need to modify a config, store server instance to stop it individually later, this is the place. +// This function can be called multiple times, depending on the number of serving schemes. +// scheme value will be set accordingly: "http", "https" or "unix" +func configureServer(s *graceful.Server, scheme string) { +} + +// The middleware configuration is for the handler executors. These do not apply to the swagger.json document. +// The middleware executes after routing but before authentication, binding and validation +func setupMiddlewares(handler http.Handler) http.Handler { + return handler +} + +// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document. +// So this is a good place to plug in a panic handling middleware, logging and metrics +func setupGlobalMiddleware(handler http.Handler) http.Handler { + return handler +} diff --git a/lib/apiservers/service/swagger.json b/lib/apiservers/service/swagger.json new file mode 100644 index 0000000000..2e7fa4be9b --- /dev/null +++ b/lib/apiservers/service/swagger.json @@ -0,0 +1,879 @@ +{ + "swagger": "2.0", + "info": { + "title": "vic-machine API", + "description": "An API for interacting with vic-machine as a RESTful web service.", + "license": { + "name": "Apache 2.0", + "url": "https://raw.githubusercontent.com/vmware/vic/master/LICENSE" + }, + "version": "v0.1.0" + }, + "basePath": "/container", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "get": { + "summary": "Show VIC metadata", + "description": "A `GET` request on the base resource will return a JSON object containing metadata including the version number of the service software and a list of known appliance ISOs.", + "responses": { + "200": { "$ref": "#/responses/metadata" } + }, + "security": [] + } + }, + "/version": { + "get": { + "summary": "Show VIC version information", + "description": "A `GET` request on the `version` sub-resource will return the version number of the service software.", + "produces": [ + "text/plain" + ], + "responses": { + "200": { "$ref": "#/responses/version" } + }, + "security": [] + } + }, + "/target/{target}": { + "get": { + "summary": "Show information about the specified vSphere resources", + "description": "Making a `GET` request on a vSphere target will return information about the state of the host firewall on those resources.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/target" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Perform an action on the specified vSphere resources", + "description": "Making a `POST` request on a vSphere target with an action of `firewall:allow` or `firewall:deny` will update the host firewall on those resources.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/target-action" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "204": { "$ref": "#/responses/success" }, + "default": { "$ref": "#/responses/error" } + } + } + }, + "/target/{target}/vch": { + "get": { + "summary": "List VCHs on the target system", + "description": "Making a `GET` request on `/vch` under a target will return information about the VCHs on that target.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/vch-list" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Create a VCH on the target system", + "description": "Making a `POST` request on `/vch` under a target will create a VCH on that target. Information about the VCH will be provided in the body of the request. Note that validation of the request will occur synchronously, with any errors being returned using an appropriate response code and status. The rest of creation will proceed asynchronously, with errors being reported via a vSphere task that is returned once the synchronous validation is complete.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "201": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + } + }, + "/target/{target}/vch/{vchId}": { + "get": { + "summary": "Get information about the target VCH", + "description": "Making a `GET` request on a VCH resource will return information about the VCH. Information about the VCH will be provided in the body of the response in the same format as create.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/vch" }, + "default": { "$ref": "#/responses/error" } + } + }, + "put": { + "summary": "Reconfigure the target VCH", + "description": "Making a `PUT` request on a VCH resource will update that VCH's configuration. Information about the VCH will be provided in the body of the request in the same format as create. Fields which cannot be modified may appear in the body of a `PUT` as long as the value of those fields match the current state of the object. When the value of a field which cannot be modified does not match the current state, a `409 Conflict` will be returned.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "patch": { + "summary": "Reconfigure the target VCH", + "description": "Making a `PATCH` request on a VCH resource (with a body as described in RFC 7396) will update a subset of that VCH's configuration. As `PATCH` is an explicit request to update a set of fields, fields which cannot be modified must not appear in the body of the `PATCH` request, even if the modification would be a no-op.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Perform an action on the target VCH", + "description": "Making a `POST` request on a VCH resource with an action of `upgrade` will initiate an upgrade of the VCH. The body of the request will be a JSON object containing the following optional properties: `bootstrap-iso` (a reference to a known bootstrap ISO on the OVA) and `rollback` (a boolean value).", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch-action" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "delete": { + "summary": "Delete the target VCH", + "description": "Making a `DELETE` request on a VCH resource will delete that VCH.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + } + }, + "/target/{target}/datacenter/{datacenter}": { + "get": { + "summary": "Show information about the specified vSphere resources", + "description": "Making a `GET` request on a datacenter will return information about the state of the host firewall on those resources.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/target" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Perform an action on the specified vSphere resources", + "description": "Making a `POST` request on a datacenter with an action of `firewall:allow` or `firewall:deny` will update the host firewall on those resources.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/target-action" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "204": { "$ref": "#/responses/success" }, + "default": { "$ref": "#/responses/error" } + } + } + }, + "/target/{target}/datacenter/{datacenter}/vch": { + "get": { + "summary": "List VCHs in the specified datacenter of the target system", + "description": "Making a `GET` request on `/vch` under a datacenter will return information about the VCHs in that datacenter.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/computeResource" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/vch-list" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Create a VCH on the target system", + "description": "Making a `POST` request on `/vch` under a datacenter will create a VCH in that datacenter. Information about the VCH will be provided in the body of the request. Note that validation of the request will occur synchronously, with any errors being returned using an appropriate response code and status. The rest of creation will proceed asynchronously, with errors being reported via a vSphere task that is returned once the synchronous validation is complete.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "201": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + } + }, + "/target/{target}/datacenter/{datacenter}/vch/{vchId}": { + "get": { + "summary": "Get information about the target VCH", + "description": "Making a `GET` request on a VCH resource will return information about the VCH. Information about the VCH will be provided in the body of the response in the same format as create.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "200": { "$ref": "#/responses/vch" }, + "default": { "$ref": "#/responses/error" } + } + }, + "put": { + "summary": "Reconfigure the target VCH", + "description": "Making a `PUT` request on a VCH resource will update that VCH's configuration. Information about the VCH will be provided in the body of the request in the same format as create. Fields which cannot be modified may appear in the body of a `PUT` as long as the value of those fields match the current state of the object. When the value of a field which cannot be modified does not match the current state, a `409 Conflict` will be returned.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "patch": { + "summary": "Reconfigure the target VCH", + "description": "Making a `PATCH` request on a VCH resource (with a body as described in RFC 7396) will update a subset of that VCH's configuration. As `PATCH` is an explicit request to update a set of fields, fields which cannot be modified must not appear in the body of the `PATCH` request, even if the modification would be a no-op.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "post": { + "summary": "Perform an action on the target VCH", + "description": "Making a `POST` request on a VCH resource with an action of `upgrade` will initiate an upgrade of the VCH. The body of the request will be a JSON object containing the following optional properties: `bootstrap-iso` (a reference to a known bootstrap ISO on the OVA) and `rollback` (a boolean value).", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/vch-action" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + }, + "delete": { + "summary": "Delete the target VCH", + "description": "Making a `DELETE` request on a VCH resource will delete that VCH.", + "parameters": [ + { "$ref": "#/parameters/target" }, + { "$ref": "#/parameters/datacenter" }, + { "$ref": "#/parameters/vch-id" }, + { "$ref": "#/parameters/thumbprint" } + ], + "responses": { + "202": { "$ref": "#/responses/vsphere-task" }, + "default": { "$ref": "#/responses/error" } + } + } + } + }, + "definitions": { + "Error": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "Version": { + "type": "string", + "pattern": "^v[0-9]+.[0-9]+.[0-9]+-[a-z0-9]+-[0-9]+-[a-f0-9]{7,40}$" + }, + "Bootstrap_Image": { + "type": "string" + }, + "Bootstrap_Image_List": { + "type": "array", + "items": { "$ref": "#/definitions/Bootstrap_Image" } + }, + "Target": { + "type": "object", + "description": "Information about the current state of a vSphere target.", + "properties": { + "firewall": { + "type": "array", + "description": "Information about the firewall status on each host.", + "items": { + "type": "object", + "properties": { + "target": { + "type": "string" + }, + "in_supported_state": { + "type": "boolean" + } + } + } + } + } + }, + "Value": { + "type": "object", + "properties": { + "units": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + }, + "Value_Bits": { + "type": "object", + "allOf": [ + { "$ref" : "#/definitions/Value" } + ], + "properties": { + "units": { + "type": "string", + "enum": [ + "bit" + ] + } + } + }, + "Value_Bytes": { + "type": "object", + "allOf": [ + { "$ref" : "#/definitions/Value" } + ], + "properties": { + "units": { + "type": "string", + "enum": [ + "B", + "KiB", + "MiB", + "GiB", + "TiB", + "PiB" + ] + } + } + }, + "Value_Hertz": { + "type": "object", + "allOf": [ + { "$ref" : "#/definitions/Value" } + ], + "properties": { + "units": { + "type": "string", + "enum": [ + "Hz", + "KHz", + "MHz", + "GHz" + ] + } + } + }, + "Shares": { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "number": { + "type": "integer" + }, + "level": { + "type": "string", + "enum": [ + "high", + "normal", + "low" + ] + } + } + }, + "Managed_Object": { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "CIDR": { + "type": "string", + "pattern": "^(([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.?){4}(\/([0-9]|[1-2][0-9]|3[0-2]))?$" + }, + "IP_Range": { "$ref": "#/definitions/CIDR" }, + "IP_Address": { + "type": "string", + "description": "TODO: see if this can just be a string with a format that captures IPv4 and IPv6?", + "format": "ipv4" + }, + "Network_Address": { + "type": "object", + "description": "TODO: see if this can just be a string with a format that captures IPv4, IPv6, and FQDNs?", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "ip": { "$ref": "#/definitions/IP_Address" }, + "hostname": { + "type": "string", + "format": "hostname" + } + } + }, + "Gateway": { + "type": "object", + "properties": { + "routing_destinations": { + "type": "array", + "items": { "$ref": "#/definitions/IP_Range" } + }, + "address": { "$ref": "#/definitions/IP_Address" } + } + }, + "Network": { + "type": "object", + "properties": { + "port_group": { "$ref": "#/definitions/Managed_Object" }, + "gateway": { "$ref": "#/definitions/Gateway" }, + "nameservers": { + "type": "array", + "items": { "$ref": "#/definitions/IP_Address" } + }, + "static": { "$ref": "#/definitions/Network_Address" } + } + }, + "Container_Network" : { + "type": "object", + "properties": { + "alias": { + "type": "string" + }, + "nameservers": { + "type": "array", + "items": { "$ref": "#/definitions/IP_Address" } + }, + "port_group": { "$ref": "#/definitions/Managed_Object" }, + "gateway": { "$ref": "#/definitions/Gateway" }, + "ip_ranges": { + "type": "array", + "items": { "$ref": "#/definitions/IP_Range" } + } + } + }, + "X509_Data": { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "pem": { + "type": "string", + "pattern": "^.*-----BEGIN [A-Z ]+-----\\s+[A-Za-z0-9+\/\\s]+={0,2}\\s-----END [A-Z ]+-----\\s*$" + } + } + }, + "VCH": { + "type": "object", + "properties": { + "version": { "$ref": "#/definitions/Version" }, + "name": { + "type": "string" + }, + "compute": { + "type": "object", + "properties": { + "cpu": { + "type": "object", + "properties": { + "limit": { "$ref": "#/definitions/Value_Hertz" }, + "reservation": { "$ref": "#/definitions/Value_Hertz" }, + "shares": { "$ref": "#/definitions/Shares" } + } + }, + "memory": { + "type": "object", + "properties": { + "limit": { "$ref": "#/definitions/Value_Bytes" }, + "reservation": { "$ref": "#/definitions/Value_Bytes" }, + "shares": { "$ref": "#/definitions/Shares" } + } + }, + "resource": { "$ref": "#/definitions/Managed_Object" } + } + }, + "network": { + "type": "object", + "properties": { + "bridge": { + "type": "object", + "properties": { + "ip_range": { "$ref": "#/definitions/IP_Range" }, + "port_group": { "$ref": "#/definitions/Managed_Object" } + } + }, + "client": { "$ref": "#/definitions/Network" }, + "management": { "$ref": "#/definitions/Network" }, + "public": { "$ref": "#/definitions/Network" }, + "container": { + "type": "array", + "items": { "$ref": "#/definitions/Container_Network" } + } + } + }, + "storage": { + "type": "object", + "properties": { + "image_stores": { + "type": "array", + "items": { + "type": "string" + } + }, + "volume_stores": { + "type": "array", + "items": { + "type": "string" + } + }, + "base_image_size": { "$ref": "#/definitions/Value_Bytes" } + } + }, + "auth": { + "type": "object", + "description": "Either `no_tls` or both `client` and `server` must be present.", + "properties": { + "no_tls": { + "type": "boolean" + }, + "client": { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "no_tls_verify": { + "type": "boolean" + }, + "certificate_authorities": { + "type": "array", + "items": { "$ref": "#/definitions/X509_Data" } + } + } + }, + "server": { + "type": "object", + "description": "Either `generate` or both `certificate` and `private_key` must be provided when creating a VCH. Only `certificate` will be present when retrieving a VCH.", + "properties": { + "certificate": { "$ref": "#/definitions/X509_Data" }, + "private_key": { "$ref": "#/definitions/X509_Data" }, + "generate": { + "type": "object", + "properties": { + "size": { "$ref": "#/definitions/Value_Bits" }, + "organization": { + "type": "array", + "items": { + "type": "string" + } + }, + "cname": { + "type": "string" + } + } + } + } + } + } + }, + "endpoint": { + "type": "object", + "properties": { + "use_resource_pool": { + "type": "boolean" + }, + "memory": { "$ref": "#/definitions/Value_Bytes" }, + "cpu": { + "type": "object", + "properties": { + "sockets": { + "type": "integer" + } + } + }, + "operations_credentials": { + "type": "object", + "properties": { + "password": { + "type": "string", + "format": "password" + }, + "user": { + "type": "string" + } + } + } + } + }, + "registry": { + "type": "object", + "properties": { + "insecure": { + "type": "array", + "items": { + "type": "string" + } + }, + "whitelist": { + "type": "array", + "items": { + "type": "string" + } + }, + "blacklist": { + "type": "array", + "items": { + "type": "string" + } + }, + "certificate_authorities": { + "type": "array", + "items": { "$ref": "#/definitions/X509_Data" } + }, + "image_fetch_proxy": { + "type": "object", + "properties": { + "http": { + "type": "string", + "format": "uri" + }, + "https": { + "type": "string", + "format": "uri" + } + } + } + } + }, + "runtime": { + "type": "object", + "properties": { + "power_state": { + "type": "string" + }, + "upgrade_status": { + "type": "string" + }, + "admin_portal": { + "type": "string" + }, + "docker_host": { + "type": "string" + } + } + } + } + }, + "VCH_List_Item": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "upgrade_status": { + "type": "string" + }, + "admin_portal": { + "type": "string" + }, + "docker_host": { + "type": "string" + } + } + }, + "VCH_List": { + "type": "array", + "items": { "$ref": "#/definitions/VCH_List_Item" } + } + }, + "parameters": { + "target": { + "name": "target", + "in": "path", + "required": true, + "type": "string" + }, + "datacenter": { + "name": "datacenter", + "in": "path", + "required": true, + "type": "string" + }, + "target-action": { + "name": "action", + "in": "query", + "description": "The action to perform on the specified vSphere resources", + "required": true, + "type": "string", + "enum": [ + "firewall:allow", + "firewall:deny" + ] + }, + "computeResource": { + "name": "compute-resource", + "in": "query", + "description": "Compute resource path", + "type": "string" + }, + "thumbprint": { + "name": "thumbprint", + "in": "query", + "description": "ESX or vCenter host certificate thumbprint", + "type": "string" + }, + "vch-id": { + "name": "vchId", + "in": "path", + "required": true, + "type": "string" + }, + "vch": { + "name": "vch", + "in": "body", + "description": "The VCH to create", + "required": true, + "schema": { + "$ref": "#/definitions/VCH" + } + }, + "vch-action": { + "name": "action", + "in": "query", + "description": "The action to perform on a VCH", + "required": true, + "type": "string", + "enum": [ + "debug", + "upgrade" + ] + } + }, + "responses": { + "success": { + "description": "A successful operation" + }, + "error": { + "description": "An error occurred", + "schema": { "$ref": "#/definitions/Error" } + }, + "metadata": { + "description": "VIC metadata information", + "schema": { + "type": "object", + "properties": { + "version": { "$ref": "#/definitions/Version" }, + "bootstrap-images": { "$ref": "#/definitions/Bootstrap_Image_List" } + } + } + }, + "target": { + "description": "A vSphere target", + "schema": { "$ref": "#/definitions/Target" } + }, + "vch": { + "description": "A VCH", + "schema": { "$ref": "#/definitions/VCH" } + }, + "vch-list": { + "description": "A list of VCHs", + "schema": { + "type": "object", + "properties": { + "vchs": { "$ref": "#/definitions/VCH_List" } + } + } + }, + "vsphere-task": { + "description": "A vSphere task", + "schema": { + "type": "object", + "properties": { + "task": { + "type": "string", + "format": "uri" + } + } + } + }, + "version": { + "description": "VIC version information.", + "schema": { + "type": "string" + }, + "examples": { + "text/plain": "v1.1.0-xxx-0-000000" + } + } + }, + "securityDefinitions": { + "basic": { + "type": "basic" + } + }, + "security": [ + {"basic": []} + ] +} diff --git a/pkg/version/version.go b/pkg/version/version.go index 6960ba4d32..5abc2ae225 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -101,6 +101,10 @@ func (v *Build) String() string { } func (v *Build) ShortVersion() string { + if v == nil { + return "unknown" + } + return fmt.Sprintf("%s-%s-%s", v.Version, v.BuildNumber, v.GitCommit) }