Adding all features provided by swagger generate and dep init.

At thing point, we've configued the swagger file and run 'dep init', which
goes out and finds all the libraries in use by the swagger runtime, downloads
them, and incorporates them into the build process.

We can run `go build ./cmd/timeofday-server` and the server will show
up in our project's base directory.  We can run the server:
`./timeofday-server --port=8020`, and then ping the endpoints:

curl http://localhost:8020
{"code":404,"message":"path / was not found"}

curl http://localhost:8020/timeofday/v1/
"operation .ClockGet has not yet been implemented"

Note that one returns an object, and the other returns a string.
Both are valid JSON objects.
This commit is contained in:
Elf M. Sternberg 2018-03-28 12:23:05 -07:00
parent 5ddc7c6a79
commit a27ae64d92
20 changed files with 2334 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/timeofday-server
/vendor

176
Gopkg.lock generated Normal file
View File

@ -0,0 +1,176 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/PuerkitoBio/purell"
packages = ["."]
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
name = "github.com/asaskevich/govalidator"
packages = ["."]
revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
version = "v9"
[[projects]]
name = "github.com/docker/go-units"
packages = ["."]
revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52"
version = "v0.3.2"
[[projects]]
branch = "master"
name = "github.com/go-openapi/analysis"
packages = ["."]
revision = "f59a71f0ece6f9dfb438be7f45148f006cbad88e"
[[projects]]
branch = "master"
name = "github.com/go-openapi/errors"
packages = ["."]
revision = "7bcb96a367bac6b76e6e42fa84155bb5581dcff8"
[[projects]]
branch = "master"
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
[[projects]]
branch = "master"
name = "github.com/go-openapi/jsonreference"
packages = ["."]
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
[[projects]]
branch = "master"
name = "github.com/go-openapi/loads"
packages = ["."]
revision = "2a2b323bab96e6b1fdee110e57d959322446e9c9"
[[projects]]
branch = "master"
name = "github.com/go-openapi/runtime"
packages = [
".",
"flagext",
"logger",
"middleware",
"middleware/denco",
"middleware/header",
"middleware/untyped",
"security"
]
revision = "62281b694b396a17fe3e4313ee8b0ca2c3cca719"
[[projects]]
branch = "master"
name = "github.com/go-openapi/spec"
packages = ["."]
revision = "9acd88844bc186c3ec7f318cd3d56f1114b4ab99"
[[projects]]
branch = "master"
name = "github.com/go-openapi/strfmt"
packages = ["."]
revision = "6ba31556a6c60db8615afb9d8eddae7aae15eb48"
[[projects]]
branch = "master"
name = "github.com/go-openapi/swag"
packages = ["."]
revision = "ceb469cb0fdf2d792f28d771bc05da6c606f55e5"
[[projects]]
branch = "master"
name = "github.com/go-openapi/validate"
packages = ["."]
revision = "180bba53b98899f743a112e568bed9e2ef31aa20"
[[projects]]
name = "github.com/jessevdk/go-flags"
packages = ["."]
revision = "96dc06278ce32a0e9d957d590bb987c81ee66407"
version = "v1.3.0"
[[projects]]
branch = "master"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
"jwriter"
]
revision = "8b799c424f57fa123fc63a99d6383bc6e4c02578"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
[[projects]]
name = "github.com/tylerb/graceful"
packages = ["."]
revision = "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb"
version = "v1.2.15"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"idna"
]
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "v2"
name = "gopkg.in/mgo.v2"
packages = [
"bson",
"internal/json"
]
revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
version = "v2.2.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d28759715da1bd6ffbf46a4622a8b6f9757e7d2a6e1568aadf0be496c42cc19c"
solver-name = "gps-cdcl"
solver-version = 1

66
Gopkg.toml Normal file
View File

@ -0,0 +1,66 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/go-openapi/errors"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/loads"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/runtime"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/spec"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/strfmt"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/swag"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/validate"
[[constraint]]
name = "github.com/jessevdk/go-flags"
version = "1.3.0"
[[constraint]]
name = "github.com/tylerb/graceful"
version = "1.2.15"
[prune]
go-tests = true
unused-packages = true

View File

@ -0,0 +1,58 @@
// Code generated by go-swagger; DO NOT EDIT.
package main
import (
"log"
"os"
loads "github.com/go-openapi/loads"
flags "github.com/jessevdk/go-flags"
"github.com/elfsternberg/timeofday/restapi"
"github.com/elfsternberg/timeofday/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.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
if err != nil {
log.Fatalln(err)
}
api := operations.NewTimeofdayAPI(swaggerSpec)
server := restapi.NewServer(api)
defer server.Shutdown()
parser := flags.NewParser(server, flags.Default)
parser.ShortDescription = "timeofday"
parser.LongDescription = "Return the time of day. Timezone optional."
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)
}
}

68
models/error_response.go Normal file
View File

@ -0,0 +1,68 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// ErrorResponse error response
// swagger:model errorResponse
type ErrorResponse struct {
// code
Code int64 `json:"code,omitempty"`
// message
// Required: true
Message *string `json:"message"`
}
// Validate validates this error response
func (m *ErrorResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateMessage(formats); err != nil {
// prop
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ErrorResponse) validateMessage(formats strfmt.Registry) error {
if err := validate.Required("message", "body", m.Message); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *ErrorResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ErrorResponse) UnmarshalBinary(b []byte) error {
var res ErrorResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

49
models/timeofday.go Normal file
View File

@ -0,0 +1,49 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
)
// Timeofday timeofday
// swagger:model timeofday
type Timeofday struct {
// timeofday
Timeofday string `json:"timeofday,omitempty"`
}
// Validate validates this timeofday
func (m *Timeofday) Validate(formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Timeofday) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Timeofday) UnmarshalBinary(b []byte) error {
var res Timeofday
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

70
models/timezone.go Normal file
View File

@ -0,0 +1,70 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Timezone timezone
// swagger:model timezone
type Timezone struct {
// timezone
// Required: true
// Min Length: 3
Timezone *string `json:"timezone"`
}
// Validate validates this timezone
func (m *Timezone) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateTimezone(formats); err != nil {
// prop
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Timezone) validateTimezone(formats strfmt.Registry) error {
if err := validate.Required("timezone", "body", m.Timezone); err != nil {
return err
}
if err := validate.MinLength("timezone", "body", string(*m.Timezone), 3); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *Timezone) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Timezone) UnmarshalBinary(b []byte) error {
var res Timezone
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@ -0,0 +1,71 @@
// This file is safe to edit. Once it exists it will not be overwritten
package restapi
import (
"crypto/tls"
"net/http"
errors "github.com/go-openapi/errors"
runtime "github.com/go-openapi/runtime"
middleware "github.com/go-openapi/runtime/middleware"
graceful "github.com/tylerb/graceful"
"github.com/elfsternberg/timeofday/restapi/operations"
)
//go:generate swagger generate server --target .. --name --spec ../swagger.yml
func configureFlags(api *operations.TimeofdayAPI) {
// api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
}
func configureAPI(api *operations.TimeofdayAPI) 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:
// api.Logger = log.Printf
api.JSONConsumer = runtime.JSONConsumer()
api.JSONProducer = runtime.JSONProducer()
api.ClockGetHandler = operations.ClockGetHandlerFunc(func(params operations.ClockGetParams) middleware.Responder {
return middleware.NotImplemented("operation .ClockGet has not yet been implemented")
})
api.ClockPostHandler = operations.ClockPostHandlerFunc(func(params operations.ClockPostParams) middleware.Responder {
return middleware.NotImplemented("operation .ClockPost 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, addr 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
}

24
restapi/doc.go Normal file
View File

@ -0,0 +1,24 @@
// Code generated by go-swagger; DO NOT EDIT.
/*
Package restapi timeofday
Return the time of day. Timezone optional.
Schemes:
http
Host: localhost
BasePath: /timeofday/v1
Version: 0.1.0
License: apache-2.0
Contact: Elf Sternberg<elf.sternberg@gmail.com> https://github.com/elfsternberg/go-timeofday
Consumes:
- application/json
Produces:
- application/json
swagger:meta
*/
package restapi

292
restapi/embedded_spec.go Normal file
View File

@ -0,0 +1,292 @@
// Code generated by go-swagger; DO NOT EDIT.
package restapi
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"encoding/json"
)
var (
// SwaggerJSON embedded version of the swagger document used at generation time
SwaggerJSON json.RawMessage
// FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
FlatSwaggerJSON json.RawMessage
)
func init() {
SwaggerJSON = json.RawMessage([]byte(`{
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"http"
],
"swagger": "2.0",
"info": {
"description": "Return the time of day. Timezone optional.",
"title": "timeofday",
"contact": {
"name": "Elf Sternberg",
"url": "https://github.com/elfsternberg/go-timeofday",
"email": "elf.sternberg@gmail.com"
},
"license": {
"name": "apache-2.0"
},
"version": "0.1.0"
},
"basePath": "/timeofday/v1",
"paths": {
"/time": {
"get": {
"description": "Returns time of day.",
"operationId": "ClockGet",
"parameters": [
{
"description": "Timezone to return",
"name": "timezone",
"in": "body",
"schema": {
"$ref": "#/definitions/timezone"
}
}
],
"responses": {
"200": {
"description": "Returns the time of day.",
"schema": {
"$ref": "#/definitions/timeofday"
}
},
"404": {
"description": "Time zone not found",
"schema": {
"$ref": "#/definitions/errorResponse"
}
},
"500": {
"description": "Something has gone horribly wrong",
"schema": {
"$ref": "#/definitions/errorResponse"
}
}
}
},
"post": {
"description": "Returns time of day.",
"operationId": "ClockPost",
"parameters": [
{
"description": "Timezone to return",
"name": "timezone",
"in": "body",
"schema": {
"$ref": "#/definitions/timezone"
}
}
],
"responses": {
"200": {
"description": "Returns the time of day.",
"schema": {
"$ref": "#/definitions/timeofday"
}
},
"404": {
"description": "Time zone not found",
"schema": {
"$ref": "#/definitions/errorResponse"
}
},
"500": {
"description": "Something has gone horribly wrong",
"schema": {
"$ref": "#/definitions/errorResponse"
}
}
}
}
}
},
"definitions": {
"errorResponse": {
"type": "object",
"required": [
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int64"
},
"message": {
"type": "string"
}
}
},
"timeofday": {
"type": "object",
"properties": {
"timeofday": {
"type": "string"
}
}
},
"timezone": {
"type": "object",
"required": [
"timezone"
],
"properties": {
"timezone": {
"type": "string",
"default": "UTC",
"minLength": 3
}
}
}
}
}`))
FlatSwaggerJSON = json.RawMessage([]byte(`{
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"http"
],
"swagger": "2.0",
"info": {
"description": "Return the time of day. Timezone optional.",
"title": "timeofday",
"contact": {
"name": "Elf Sternberg",
"url": "https://github.com/elfsternberg/go-timeofday",
"email": "elf.sternberg@gmail.com"
},
"license": {
"name": "apache-2.0"
},
"version": "0.1.0"
},
"basePath": "/timeofday/v1",
"paths": {
"/time": {
"get": {
"description": "Returns time of day.",
"operationId": "ClockGet",
"parameters": [
{
"description": "Timezone to return",
"name": "timezone",
"in": "body",
"schema": {
"$ref": "#/definitions/timezone"
}
}
],
"responses": {
"200": {
"description": "Returns the time of day.",
"schema": {
"$ref": "#/definitions/timeofday"
}
},
"404": {
"description": "Time zone not found",
"schema": {
"$ref": "#/definitions/errorResponse"
}
},
"500": {
"description": "Something has gone horribly wrong",
"schema": {
"$ref": "#/definitions/errorResponse"
}
}
}
},
"post": {
"description": "Returns time of day.",
"operationId": "ClockPost",
"parameters": [
{
"description": "Timezone to return",
"name": "timezone",
"in": "body",
"schema": {
"$ref": "#/definitions/timezone"
}
}
],
"responses": {
"200": {
"description": "Returns the time of day.",
"schema": {
"$ref": "#/definitions/timeofday"
}
},
"404": {
"description": "Time zone not found",
"schema": {
"$ref": "#/definitions/errorResponse"
}
},
"500": {
"description": "Something has gone horribly wrong",
"schema": {
"$ref": "#/definitions/errorResponse"
}
}
}
}
}
},
"definitions": {
"errorResponse": {
"type": "object",
"required": [
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int64"
},
"message": {
"type": "string"
}
}
},
"timeofday": {
"type": "object",
"properties": {
"timeofday": {
"type": "string"
}
}
},
"timezone": {
"type": "object",
"required": [
"timezone"
],
"properties": {
"timezone": {
"type": "string",
"default": "UTC",
"minLength": 3
}
}
}
}
}`))
}

View File

@ -0,0 +1,58 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
middleware "github.com/go-openapi/runtime/middleware"
)
// ClockGetHandlerFunc turns a function with the right signature into a clock get handler
type ClockGetHandlerFunc func(ClockGetParams) middleware.Responder
// Handle executing the request and returning a response
func (fn ClockGetHandlerFunc) Handle(params ClockGetParams) middleware.Responder {
return fn(params)
}
// ClockGetHandler interface for that can handle valid clock get params
type ClockGetHandler interface {
Handle(ClockGetParams) middleware.Responder
}
// NewClockGet creates a new http.Handler for the clock get operation
func NewClockGet(ctx *middleware.Context, handler ClockGetHandler) *ClockGet {
return &ClockGet{Context: ctx, Handler: handler}
}
/*ClockGet swagger:route GET /time clockGet
Returns time of day.
*/
type ClockGet struct {
Context *middleware.Context
Handler ClockGetHandler
}
func (o *ClockGet) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewClockGetParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@ -0,0 +1,70 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
models "github.com/elfsternberg/timeofday/models"
)
// NewClockGetParams creates a new ClockGetParams object
// no default values defined in spec.
func NewClockGetParams() ClockGetParams {
return ClockGetParams{}
}
// ClockGetParams contains all the bound params for the clock get operation
// typically these are obtained from a http.Request
//
// swagger:parameters ClockGet
type ClockGetParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Timezone to return
In: body
*/
Timezone *models.Timezone
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewClockGetParams() beforehand.
func (o *ClockGetParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.Timezone
if err := route.Consumer.Consume(r.Body, &body); err != nil {
res = append(res, errors.NewParseError("timezone", "body", "", err))
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Timezone = &body
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@ -0,0 +1,146 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
models "github.com/elfsternberg/timeofday/models"
)
// ClockGetOKCode is the HTTP code returned for type ClockGetOK
const ClockGetOKCode int = 200
/*ClockGetOK Returns the time of day.
swagger:response clockGetOK
*/
type ClockGetOK struct {
/*
In: Body
*/
Payload *models.Timeofday `json:"body,omitempty"`
}
// NewClockGetOK creates ClockGetOK with default headers values
func NewClockGetOK() *ClockGetOK {
return &ClockGetOK{}
}
// WithPayload adds the payload to the clock get o k response
func (o *ClockGetOK) WithPayload(payload *models.Timeofday) *ClockGetOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock get o k response
func (o *ClockGetOK) SetPayload(payload *models.Timeofday) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockGetOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// ClockGetNotFoundCode is the HTTP code returned for type ClockGetNotFound
const ClockGetNotFoundCode int = 404
/*ClockGetNotFound Time zone not found
swagger:response clockGetNotFound
*/
type ClockGetNotFound struct {
/*
In: Body
*/
Payload *models.ErrorResponse `json:"body,omitempty"`
}
// NewClockGetNotFound creates ClockGetNotFound with default headers values
func NewClockGetNotFound() *ClockGetNotFound {
return &ClockGetNotFound{}
}
// WithPayload adds the payload to the clock get not found response
func (o *ClockGetNotFound) WithPayload(payload *models.ErrorResponse) *ClockGetNotFound {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock get not found response
func (o *ClockGetNotFound) SetPayload(payload *models.ErrorResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockGetNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(404)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// ClockGetInternalServerErrorCode is the HTTP code returned for type ClockGetInternalServerError
const ClockGetInternalServerErrorCode int = 500
/*ClockGetInternalServerError Something has gone horribly wrong
swagger:response clockGetInternalServerError
*/
type ClockGetInternalServerError struct {
/*
In: Body
*/
Payload *models.ErrorResponse `json:"body,omitempty"`
}
// NewClockGetInternalServerError creates ClockGetInternalServerError with default headers values
func NewClockGetInternalServerError() *ClockGetInternalServerError {
return &ClockGetInternalServerError{}
}
// WithPayload adds the payload to the clock get internal server error response
func (o *ClockGetInternalServerError) WithPayload(payload *models.ErrorResponse) *ClockGetInternalServerError {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock get internal server error response
func (o *ClockGetInternalServerError) SetPayload(payload *models.ErrorResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockGetInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(500)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@ -0,0 +1,87 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// ClockGetURL generates an URL for the clock get operation
type ClockGetURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ClockGetURL) WithBasePath(bp string) *ClockGetURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ClockGetURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *ClockGetURL) Build() (*url.URL, error) {
var result url.URL
var _path = "/time"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/timeofday/v1"
}
result.Path = golangswaggerpaths.Join(_basePath, _path)
return &result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *ClockGetURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *ClockGetURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *ClockGetURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on ClockGetURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on ClockGetURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *ClockGetURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@ -0,0 +1,58 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
middleware "github.com/go-openapi/runtime/middleware"
)
// ClockPostHandlerFunc turns a function with the right signature into a clock post handler
type ClockPostHandlerFunc func(ClockPostParams) middleware.Responder
// Handle executing the request and returning a response
func (fn ClockPostHandlerFunc) Handle(params ClockPostParams) middleware.Responder {
return fn(params)
}
// ClockPostHandler interface for that can handle valid clock post params
type ClockPostHandler interface {
Handle(ClockPostParams) middleware.Responder
}
// NewClockPost creates a new http.Handler for the clock post operation
func NewClockPost(ctx *middleware.Context, handler ClockPostHandler) *ClockPost {
return &ClockPost{Context: ctx, Handler: handler}
}
/*ClockPost swagger:route POST /time clockPost
Returns time of day.
*/
type ClockPost struct {
Context *middleware.Context
Handler ClockPostHandler
}
func (o *ClockPost) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewClockPostParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@ -0,0 +1,70 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
models "github.com/elfsternberg/timeofday/models"
)
// NewClockPostParams creates a new ClockPostParams object
// no default values defined in spec.
func NewClockPostParams() ClockPostParams {
return ClockPostParams{}
}
// ClockPostParams contains all the bound params for the clock post operation
// typically these are obtained from a http.Request
//
// swagger:parameters ClockPost
type ClockPostParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Timezone to return
In: body
*/
Timezone *models.Timezone
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewClockPostParams() beforehand.
func (o *ClockPostParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.Timezone
if err := route.Consumer.Consume(r.Body, &body); err != nil {
res = append(res, errors.NewParseError("timezone", "body", "", err))
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Timezone = &body
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@ -0,0 +1,146 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
models "github.com/elfsternberg/timeofday/models"
)
// ClockPostOKCode is the HTTP code returned for type ClockPostOK
const ClockPostOKCode int = 200
/*ClockPostOK Returns the time of day.
swagger:response clockPostOK
*/
type ClockPostOK struct {
/*
In: Body
*/
Payload *models.Timeofday `json:"body,omitempty"`
}
// NewClockPostOK creates ClockPostOK with default headers values
func NewClockPostOK() *ClockPostOK {
return &ClockPostOK{}
}
// WithPayload adds the payload to the clock post o k response
func (o *ClockPostOK) WithPayload(payload *models.Timeofday) *ClockPostOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock post o k response
func (o *ClockPostOK) SetPayload(payload *models.Timeofday) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockPostOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// ClockPostNotFoundCode is the HTTP code returned for type ClockPostNotFound
const ClockPostNotFoundCode int = 404
/*ClockPostNotFound Time zone not found
swagger:response clockPostNotFound
*/
type ClockPostNotFound struct {
/*
In: Body
*/
Payload *models.ErrorResponse `json:"body,omitempty"`
}
// NewClockPostNotFound creates ClockPostNotFound with default headers values
func NewClockPostNotFound() *ClockPostNotFound {
return &ClockPostNotFound{}
}
// WithPayload adds the payload to the clock post not found response
func (o *ClockPostNotFound) WithPayload(payload *models.ErrorResponse) *ClockPostNotFound {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock post not found response
func (o *ClockPostNotFound) SetPayload(payload *models.ErrorResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockPostNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(404)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// ClockPostInternalServerErrorCode is the HTTP code returned for type ClockPostInternalServerError
const ClockPostInternalServerErrorCode int = 500
/*ClockPostInternalServerError Something has gone horribly wrong
swagger:response clockPostInternalServerError
*/
type ClockPostInternalServerError struct {
/*
In: Body
*/
Payload *models.ErrorResponse `json:"body,omitempty"`
}
// NewClockPostInternalServerError creates ClockPostInternalServerError with default headers values
func NewClockPostInternalServerError() *ClockPostInternalServerError {
return &ClockPostInternalServerError{}
}
// WithPayload adds the payload to the clock post internal server error response
func (o *ClockPostInternalServerError) WithPayload(payload *models.ErrorResponse) *ClockPostInternalServerError {
o.Payload = payload
return o
}
// SetPayload sets the payload to the clock post internal server error response
func (o *ClockPostInternalServerError) SetPayload(payload *models.ErrorResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ClockPostInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(500)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@ -0,0 +1,87 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// ClockPostURL generates an URL for the clock post operation
type ClockPostURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ClockPostURL) WithBasePath(bp string) *ClockPostURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ClockPostURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *ClockPostURL) Build() (*url.URL, error) {
var result url.URL
var _path = "/time"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/timeofday/v1"
}
result.Path = golangswaggerpaths.Join(_basePath, _path)
return &result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *ClockPostURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *ClockPostURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *ClockPostURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on ClockPostURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on ClockPostURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *ClockPostURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@ -0,0 +1,288 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"net/http"
"strings"
errors "github.com/go-openapi/errors"
loads "github.com/go-openapi/loads"
runtime "github.com/go-openapi/runtime"
middleware "github.com/go-openapi/runtime/middleware"
security "github.com/go-openapi/runtime/security"
spec "github.com/go-openapi/spec"
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// NewTimeofdayAPI creates a new Timeofday instance
func NewTimeofdayAPI(spec *loads.Document) *TimeofdayAPI {
return &TimeofdayAPI{
handlers: make(map[string]map[string]http.Handler),
formats: strfmt.Default,
defaultConsumes: "application/json",
defaultProduces: "application/json",
customConsumers: make(map[string]runtime.Consumer),
customProducers: make(map[string]runtime.Producer),
ServerShutdown: func() {},
spec: spec,
ServeError: errors.ServeError,
BasicAuthenticator: security.BasicAuth,
APIKeyAuthenticator: security.APIKeyAuth,
BearerAuthenticator: security.BearerAuth,
JSONConsumer: runtime.JSONConsumer(),
JSONProducer: runtime.JSONProducer(),
ClockGetHandler: ClockGetHandlerFunc(func(params ClockGetParams) middleware.Responder {
return middleware.NotImplemented("operation ClockGet has not yet been implemented")
}),
ClockPostHandler: ClockPostHandlerFunc(func(params ClockPostParams) middleware.Responder {
return middleware.NotImplemented("operation ClockPost has not yet been implemented")
}),
}
}
/*TimeofdayAPI Return the time of day. Timezone optional. */
type TimeofdayAPI struct {
spec *loads.Document
context *middleware.Context
handlers map[string]map[string]http.Handler
formats strfmt.Registry
customConsumers map[string]runtime.Consumer
customProducers map[string]runtime.Producer
defaultConsumes string
defaultProduces string
Middleware func(middleware.Builder) http.Handler
// BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
// APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
// BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
// It has a default implemention in the security package, however you can replace it for your particular usage.
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
// JSONConsumer registers a consumer for a "application/json" mime type
JSONConsumer runtime.Consumer
// JSONProducer registers a producer for a "application/json" mime type
JSONProducer runtime.Producer
// ClockGetHandler sets the operation handler for the clock get operation
ClockGetHandler ClockGetHandler
// ClockPostHandler sets the operation handler for the clock post operation
ClockPostHandler ClockPostHandler
// ServeError is called when an error is received, there is a default handler
// but you can set your own with this
ServeError func(http.ResponseWriter, *http.Request, error)
// ServerShutdown is called when the HTTP(S) server is shut down and done
// handling all active connections and does not accept connections any more
ServerShutdown func()
// Custom command line argument groups with their descriptions
CommandLineOptionsGroups []swag.CommandLineOptionsGroup
// User defined logger function.
Logger func(string, ...interface{})
}
// SetDefaultProduces sets the default produces media type
func (o *TimeofdayAPI) SetDefaultProduces(mediaType string) {
o.defaultProduces = mediaType
}
// SetDefaultConsumes returns the default consumes media type
func (o *TimeofdayAPI) SetDefaultConsumes(mediaType string) {
o.defaultConsumes = mediaType
}
// SetSpec sets a spec that will be served for the clients.
func (o *TimeofdayAPI) SetSpec(spec *loads.Document) {
o.spec = spec
}
// DefaultProduces returns the default produces media type
func (o *TimeofdayAPI) DefaultProduces() string {
return o.defaultProduces
}
// DefaultConsumes returns the default consumes media type
func (o *TimeofdayAPI) DefaultConsumes() string {
return o.defaultConsumes
}
// Formats returns the registered string formats
func (o *TimeofdayAPI) Formats() strfmt.Registry {
return o.formats
}
// RegisterFormat registers a custom format validator
func (o *TimeofdayAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
o.formats.Add(name, format, validator)
}
// Validate validates the registrations in the TimeofdayAPI
func (o *TimeofdayAPI) Validate() error {
var unregistered []string
if o.JSONConsumer == nil {
unregistered = append(unregistered, "JSONConsumer")
}
if o.JSONProducer == nil {
unregistered = append(unregistered, "JSONProducer")
}
if o.ClockGetHandler == nil {
unregistered = append(unregistered, "ClockGetHandler")
}
if o.ClockPostHandler == nil {
unregistered = append(unregistered, "ClockPostHandler")
}
if len(unregistered) > 0 {
return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
}
return nil
}
// ServeErrorFor gets a error handler for a given operation id
func (o *TimeofdayAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
return o.ServeError
}
// AuthenticatorsFor gets the authenticators for the specified security schemes
func (o *TimeofdayAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
return nil
}
// Authorizer returns the registered authorizer
func (o *TimeofdayAPI) Authorizer() runtime.Authorizer {
return nil
}
// ConsumersFor gets the consumers for the specified media types
func (o *TimeofdayAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
result := make(map[string]runtime.Consumer)
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONConsumer
}
if c, ok := o.customConsumers[mt]; ok {
result[mt] = c
}
}
return result
}
// ProducersFor gets the producers for the specified media types
func (o *TimeofdayAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
result := make(map[string]runtime.Producer)
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONProducer
}
if p, ok := o.customProducers[mt]; ok {
result[mt] = p
}
}
return result
}
// HandlerFor gets a http.Handler for the provided operation method and path
func (o *TimeofdayAPI) HandlerFor(method, path string) (http.Handler, bool) {
if o.handlers == nil {
return nil, false
}
um := strings.ToUpper(method)
if _, ok := o.handlers[um]; !ok {
return nil, false
}
if path == "/" {
path = ""
}
h, ok := o.handlers[um][path]
return h, ok
}
// Context returns the middleware context for the timeofday API
func (o *TimeofdayAPI) Context() *middleware.Context {
if o.context == nil {
o.context = middleware.NewRoutableContext(o.spec, o, nil)
}
return o.context
}
func (o *TimeofdayAPI) initHandlerCache() {
o.Context() // don't care about the result, just that the initialization happened
if o.handlers == nil {
o.handlers = make(map[string]map[string]http.Handler)
}
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/time"] = NewClockGet(o.context, o.ClockGetHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/time"] = NewClockPost(o.context, o.ClockPostHandler)
}
// Serve creates a http handler to serve the API over HTTP
// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
func (o *TimeofdayAPI) Serve(builder middleware.Builder) http.Handler {
o.Init()
if o.Middleware != nil {
return o.Middleware(builder)
}
return o.context.APIHandler(builder)
}
// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
func (o *TimeofdayAPI) Init() {
if len(o.handlers) == 0 {
o.initHandlerCache()
}
}
// RegisterConsumer allows you to add (or override) a consumer for a media type.
func (o *TimeofdayAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
o.customConsumers[mediaType] = consumer
}
// RegisterProducer allows you to add (or override) a producer for a media type.
func (o *TimeofdayAPI) RegisterProducer(mediaType string, producer runtime.Producer) {
o.customProducers[mediaType] = producer
}

447
restapi/server.go Normal file
View File

@ -0,0 +1,447 @@
// Code generated by go-swagger; DO NOT EDIT.
package restapi
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/go-openapi/runtime/flagext"
"github.com/go-openapi/swag"
flags "github.com/jessevdk/go-flags"
graceful "github.com/tylerb/graceful"
"github.com/elfsternberg/timeofday/restapi/operations"
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
schemeUnix = "unix"
)
var defaultSchemes []string
func init() {
defaultSchemes = []string{
schemeHTTP,
}
}
// NewServer creates a new api timeofday server but does not configure it
func NewServer(api *operations.TimeofdayAPI) *Server {
s := new(Server)
s.shutdown = make(chan struct{})
s.api = api
return s
}
// ConfigureAPI configures the API and handlers.
func (s *Server) ConfigureAPI() {
if s.api != nil {
s.handler = configureAPI(s.api)
}
}
// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
func (s *Server) ConfigureFlags() {
if s.api != nil {
configureFlags(s.api)
}
}
// Server for the timeofday API
type Server struct {
EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before shutting down the server" default:"10s"`
MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/timeofday.sock"`
domainSocketL net.Listener
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
httpServerL net.Listener
TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"`
TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
httpsServerL net.Listener
api *operations.TimeofdayAPI
handler http.Handler
hasListeners bool
shutdown chan struct{}
shuttingDown int32
}
// Logf logs message either via defined user logger or via system one if no user logger is defined.
func (s *Server) Logf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
} else {
log.Printf(f, args...)
}
}
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
// Exits with non-zero status after printing
func (s *Server) Fatalf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
os.Exit(1)
} else {
log.Fatalf(f, args...)
}
}
// SetAPI configures the server with the specified API. Needs to be called before Serve
func (s *Server) SetAPI(api *operations.TimeofdayAPI) {
if api == nil {
s.api = nil
s.handler = nil
return
}
s.api = api
s.api.Logger = log.Printf
s.handler = configureAPI(api)
}
func (s *Server) hasScheme(scheme string) bool {
schemes := s.EnabledListeners
if len(schemes) == 0 {
schemes = defaultSchemes
}
for _, v := range schemes {
if v == scheme {
return true
}
}
return false
}
// Serve the api
func (s *Server) Serve() (err error) {
if !s.hasListeners {
if err = s.Listen(); err != nil {
return err
}
}
// set default handler, if none is set
if s.handler == nil {
if s.api == nil {
return errors.New("can't create the default handler, as no api is set")
}
s.SetHandler(s.api.Serve(nil))
}
var wg sync.WaitGroup
if s.hasScheme(schemeUnix) {
domainSocket := &graceful.Server{Server: new(http.Server)}
domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
domainSocket.Handler = s.handler
domainSocket.LogFunc = s.Logf
if int64(s.CleanupTimeout) > 0 {
domainSocket.Timeout = s.CleanupTimeout
}
configureServer(domainSocket, "unix", string(s.SocketPath))
wg.Add(2)
s.Logf("Serving timeofday at unix://%s", s.SocketPath)
go func(l net.Listener) {
defer wg.Done()
if err := domainSocket.Serve(l); err != nil {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving timeofday at unix://%s", s.SocketPath)
}(s.domainSocketL)
go s.handleShutdown(&wg, domainSocket)
}
if s.hasScheme(schemeHTTP) {
httpServer := &graceful.Server{Server: new(http.Server)}
httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpServer.ReadTimeout = s.ReadTimeout
httpServer.WriteTimeout = s.WriteTimeout
httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
httpServer.TCPKeepAlive = s.KeepAlive
if s.ListenLimit > 0 {
httpServer.ListenLimit = s.ListenLimit
}
if int64(s.CleanupTimeout) > 0 {
httpServer.Timeout = s.CleanupTimeout
}
httpServer.Handler = s.handler
httpServer.LogFunc = s.Logf
configureServer(httpServer, "http", s.httpServerL.Addr().String())
wg.Add(2)
s.Logf("Serving timeofday at http://%s", s.httpServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpServer.Serve(l); err != nil {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving timeofday at http://%s", l.Addr())
}(s.httpServerL)
go s.handleShutdown(&wg, httpServer)
}
if s.hasScheme(schemeHTTPS) {
httpsServer := &graceful.Server{Server: new(http.Server)}
httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpsServer.ReadTimeout = s.TLSReadTimeout
httpsServer.WriteTimeout = s.TLSWriteTimeout
httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
httpsServer.TCPKeepAlive = s.TLSKeepAlive
if s.TLSListenLimit > 0 {
httpsServer.ListenLimit = s.TLSListenLimit
}
if int64(s.CleanupTimeout) > 0 {
httpsServer.Timeout = s.CleanupTimeout
}
httpsServer.Handler = s.handler
httpsServer.LogFunc = s.Logf
// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
httpsServer.TLSConfig = &tls.Config{
// Causes servers to use Go's default ciphersuite preferences,
// which are tuned to avoid attacks. Does nothing on clients.
PreferServerCipherSuites: true,
// Only use curves which have assembly implementations
// https://github.com/golang/go/tree/master/src/crypto/elliptic
CurvePreferences: []tls.CurveID{tls.CurveP256},
// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
NextProtos: []string{"http/1.1", "h2"},
// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
MinVersion: tls.VersionTLS12,
// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
}
if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
}
if s.TLSCACertificate != "" {
caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate))
if caCertErr != nil {
log.Fatal(caCertErr)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
httpsServer.TLSConfig.ClientCAs = caCertPool
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
configureTLS(httpsServer.TLSConfig)
httpsServer.TLSConfig.BuildNameToCertificate()
if err != nil {
return err
}
if len(httpsServer.TLSConfig.Certificates) == 0 {
if s.TLSCertificate == "" {
if s.TLSCertificateKey == "" {
s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
}
s.Fatalf("the required flag `--tls-certificate` was not specified")
}
if s.TLSCertificateKey == "" {
s.Fatalf("the required flag `--tls-key` was not specified")
}
}
configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
wg.Add(2)
s.Logf("Serving timeofday at https://%s", s.httpsServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpsServer.Serve(l); err != nil {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving timeofday at https://%s", l.Addr())
}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
go s.handleShutdown(&wg, httpsServer)
}
wg.Wait()
return nil
}
// Listen creates the listeners for the server
func (s *Server) Listen() error {
if s.hasListeners { // already done this
return nil
}
if s.hasScheme(schemeHTTPS) {
// Use http host if https host wasn't defined
if s.TLSHost == "" {
s.TLSHost = s.Host
}
// Use http listen limit if https listen limit wasn't defined
if s.TLSListenLimit == 0 {
s.TLSListenLimit = s.ListenLimit
}
// Use http tcp keep alive if https tcp keep alive wasn't defined
if int64(s.TLSKeepAlive) == 0 {
s.TLSKeepAlive = s.KeepAlive
}
// Use http read timeout if https read timeout wasn't defined
if int64(s.TLSReadTimeout) == 0 {
s.TLSReadTimeout = s.ReadTimeout
}
// Use http write timeout if https write timeout wasn't defined
if int64(s.TLSWriteTimeout) == 0 {
s.TLSWriteTimeout = s.WriteTimeout
}
}
if s.hasScheme(schemeUnix) {
domSockListener, err := net.Listen("unix", string(s.SocketPath))
if err != nil {
return err
}
s.domainSocketL = domSockListener
}
if s.hasScheme(schemeHTTP) {
listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
if err != nil {
return err
}
h, p, err := swag.SplitHostPort(listener.Addr().String())
if err != nil {
return err
}
s.Host = h
s.Port = p
s.httpServerL = listener
}
if s.hasScheme(schemeHTTPS) {
tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
if err != nil {
return err
}
sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
if err != nil {
return err
}
s.TLSHost = sh
s.TLSPort = sp
s.httpsServerL = tlsListener
}
s.hasListeners = true
return nil
}
// Shutdown server and clean up resources
func (s *Server) Shutdown() error {
if atomic.LoadInt32(&s.shuttingDown) != 0 {
s.Logf("already shutting down")
return nil
}
s.shutdown <- struct{}{}
return nil
}
func (s *Server) handleShutdown(wg *sync.WaitGroup, server *graceful.Server) {
defer wg.Done()
for {
select {
case <-s.shutdown:
atomic.AddInt32(&s.shuttingDown, 1)
server.Stop(s.CleanupTimeout)
<-server.StopChan()
s.api.ServerShutdown()
return
case <-server.StopChan():
atomic.AddInt32(&s.shuttingDown, 1)
s.api.ServerShutdown()
return
}
}
}
// GetHandler returns a handler useful for testing
func (s *Server) GetHandler() http.Handler {
return s.handler
}
// SetHandler allows for setting a http handler on this server
func (s *Server) SetHandler(handler http.Handler) {
s.handler = handler
}
// UnixListener returns the domain socket listener
func (s *Server) UnixListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.domainSocketL, nil
}
// HTTPListener returns the http listener
func (s *Server) HTTPListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpServerL, nil
}
// TLSListener returns the https listener
func (s *Server) TLSListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpsServerL, nil
}