Restructure project similar to hello and graph

This commit is contained in:
Thomas Boerger
2019-12-09 16:12:53 +01:00
parent f9f58f9a32
commit 3498580b7b
65 changed files with 2855 additions and 1054 deletions
-33
View File
@@ -1,33 +0,0 @@
[run]
watch_all = true
watch_dirs = ["cmd", "pkg"]
watch_exts = [".go"]
ignore = [".git", "bin", "dist"]
ignore_files = []
build_delay = 1500
interrupt_timout = 15
graceful_kill = false
init_cmds = [
["make", "build"],
[
"./bin/ocis-phoenix",
"--log-level",
"debug",
"server",
"--asset-path",
"assets/"
]
]
cmds = [
["make", "build"],
[
"./bin/ocis-phoenix",
"--log-level",
"debug",
"server",
"--asset-path",
"assets/"
]
]
+8
View File
@@ -0,0 +1,8 @@
---
exclude_paths:
- CHANGELOG.md
- changelog/**
- docs/**
- pkg/proto/**
...
+25 -2
View File
@@ -449,10 +449,33 @@ def changelog(ctx):
'os': 'linux',
'arch': 'amd64',
},
'clone': {
'disable': True,
},
'steps': [
{
'name': 'clone',
'image': 'plugins/git-action:1',
'pull': 'always',
'settings': {
'actions': [
'clone',
],
'remote': 'https://github.com/%s' % (ctx.repo.slug),
'branch': ctx.build.branch if ctx.build.event == 'pull_request' else 'master',
'path': '/drone/src',
'netrc_machine': 'github.com',
'netrc_username': {
'from_secret': 'github_username',
},
'netrc_password': {
'from_secret': 'github_token',
},
},
},
{
'name': 'generate',
'image': 'toolhippie/calens:latest',
'image': 'webhippie/golang:1.13',
'pull': 'always',
'commands': [
'make changelog',
@@ -460,7 +483,7 @@ def changelog(ctx):
},
{
'name': 'output',
'image': 'toolhippie/calens:latest',
'image': 'webhippie/golang:1.13',
'pull': 'always',
'commands': [
'cat CHANGELOG.md',
+9 -1
View File
@@ -19,7 +19,15 @@ indent_size = 4
indent_style = space
indent_size = 2
[*.yml]
[*.{yml,json}]
indent_style = space
indent_size = 2
[*.{js,vue}]
indent_style = space
indent_size = 2
[*.{css,less}]
indent_style = space
indent_size = 2
+1 -1
View File
@@ -1,7 +1,7 @@
---
repository:
name: ocis-phoenix
description: ':atom_symbol: Serve Phoenix for Reva/oCIS'
description: ':atom_symbol: Serve Phoenix for oCIS'
homepage: https://owncloud.github.io/ocis-phoenix/
topics: reva, ocis
-2
View File
@@ -4,5 +4,3 @@ coverage.out
/dist
/assets
/pkg/assets/embed.go
+18 -1
View File
@@ -1 +1,18 @@
# Changelog
# Changelog for unreleased
The following sections list the changes for unreleased.
## Summary
* Chg #3: Initial release of basic version
## Details
* Change #3: Initial release of basic version
Just prepared an initial basic version to serve Phoenix for the ownCloud Infinite Scale
project. It just provides a minimal viable product to demonstrate the microservice pattern.
https://github.com/owncloud/ocis-phoenix/issues/3
+60 -5
View File
@@ -6,13 +6,21 @@ DIST := dist
ifeq ($(OS), Windows_NT)
EXECUTABLE := $(NAME).exe
UNAME := Windows
else
EXECUTABLE := $(NAME)
UNAME := $(shell uname -s)
endif
ifeq ($(UNAME), Darwin)
GOBUILD ?= go build -i
else
GOBUILD ?= go build
endif
PACKAGES ?= $(shell go list ./...)
SOURCES ?= $(shell find . -name "*.go" -type f)
GENERATE ?= $(IMPORT)/pkg/assets
SOURCES ?= $(shell find . -name "*.go" -type f -not -path "./node_modules/*")
GENERATE ?= $(PACKAGES)
TAGS ?=
@@ -37,6 +45,7 @@ ifndef DATE
endif
LDFLAGS += -s -w -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)"
GCFLAGS += all=-N -l
.PHONY: all
all: build
@@ -48,7 +57,7 @@ sync:
.PHONY: clean
clean:
go clean -i ./...
rm -rf $(BIN) $(DIST) assets/
rm -rf $(BIN) $(DIST)
.PHONY: fmt
fmt:
@@ -83,10 +92,13 @@ install: $(SOURCES)
go install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/$(NAME)
.PHONY: build
build: $(BIN)/$(EXECUTABLE)
build: $(BIN)/$(EXECUTABLE) $(BIN)/$(EXECUTABLE)-debug
$(BIN)/$(EXECUTABLE): $(SOURCES)
go build -i -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ ./cmd/$(NAME)
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ ./cmd/$(NAME)
$(BIN)/$(EXECUTABLE)-debug: $(SOURCES)
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -gcflags '$(GCFLAGS)' -o $@ ./cmd/$(NAME)
.PHONY: release
release: release-dirs release-linux release-windows release-darwin release-copy release-check
@@ -122,5 +134,48 @@ release-finish: release-copy release-check
docs:
cd docs; hugo
.PHONY: watch
watch:
go run github.com/cespare/reflex -c reflex.conf
# $(GOPATH)/bin/protoc-gen-go:
# GO111MODULE=off go get -v github.com/golang/protobuf/protoc-gen-go
# $(GOPATH)/bin/protoc-gen-micro:
# GO111MODULE=off go get -v github.com/micro/protoc-gen-micro
# $(GOPATH)/bin/protoc-gen-microweb:
# GO111MODULE=off go get -v github.com/webhippie/protoc-gen-microweb
# $(GOPATH)/bin/protoc-gen-swagger:
# GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
# pkg/proto/v0/example.pb.go: pkg/proto/v0/example.proto
# protoc \
# -I=third_party/ \
# -I=pkg/proto/v0/ \
# --go_out=logtostderr=true:pkg/proto/v0 example.proto
# pkg/proto/v0/example.pb.micro.go: pkg/proto/v0/example.proto
# protoc \
# -I=third_party/ \
# -I=pkg/proto/v0/ \
# --micro_out=logtostderr=true:pkg/proto/v0 example.proto
# pkg/proto/v0/example.pb.web.go: pkg/proto/v0/example.proto
# protoc \
# -I=third_party/ \
# -I=pkg/proto/v0/ \
# --microweb_out=logtostderr=true:pkg/proto/v0 example.proto
# pkg/proto/v0/example.swagger.json: pkg/proto/v0/example.proto
# protoc \
# -I=third_party/ \
# -I=pkg/proto/v0/ \
# --swagger_out=logtostderr=true:pkg/proto/v0 example.proto
# .PHONY: protobuf
# protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-swagger pkg/proto/v0/example.pb.go pkg/proto/v0/example.pb.micro.go pkg/proto/v0/example.pb.web.go pkg/proto/v0/example.swagger.json
assets:
mkdir assets/ && curl -slL -o- https://github.com/owncloud/phoenix/releases/download/0.2.5/phoenix.tar.gz | tar xvzf - -C assets/
+2 -2
View File
@@ -11,11 +11,11 @@
## Install
You can download prebuilt binaries from the GitHub releases or from our [download mirrors](http://download.owncloud.com/ocis/phoenix/). For instructions how to install this on your platform you should take a look at our [documentation](https://owncloud.github.cio/ocis-phoenix/)
You can download prebuilt binaries from the GitHub releases or from our [download mirrors](http://download.owncloud.com/ocis/phoenix/). For instructions how to install this on your platform you should take a look at our [documentation](https://owncloud.github.io/ocis-phoenix/)
## Development
Make sure you have a working Go environment, for further reference or a guide take a look at the [install instructions](http://golang.org/doc/install.html). This project requires Go >= v1.12.
Make sure you have a working Go environment, for further reference or a guide take a look at the [install instructions](http://golang.org/doc/install.html). This project requires Go >= v1.12. For the frontend it's also required to have [NodeJS](https://nodejs.org/en/download/package-manager/) and [Yarn](https://yarnpkg.com/lang/en/docs/install/) installed.
```console
git clone https://github.com/owncloud/ocis-phoenix.git
View File
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func main() {
if err := command.Root().Execute(); err != nil {
if err := command.Execute(); err != nil {
os.Exit(1)
}
}
+22 -15
View File
@@ -1,17 +1,24 @@
{
"debug": {
"addr": "0.0.0.0:8090",
"token": "",
"pprof": false
},
"http": {
"addr": "0.0.0.0:8080",
"root": "/"
},
"asset": {
"path": ""
},
"config": {
"file": ""
}
"debug": {
"addr": "0.0.0.0:9104",
"token": "",
"pprof": false,
"zpages": false
},
"http": {
"addr": "0.0.0.0:9100"
},
"tracing": {
"enabled": false,
"type": "jaeger",
"endpoint": "localhost:6831",
"collector": "http://localhost:14268/api/traces",
"service": "phoenix"
},
"asset": {
"path": ""
},
"config": {
"file": ""
}
}
+10 -3
View File
@@ -1,12 +1,19 @@
---
debug:
addr: 0.0.0.0:8090
addr: 0.0.0.0:9104
token:
pprof: false
zpages: false
http:
addr: 0.0.0.0:8080
root: /
addr: 0.0.0.0:9100
tracing:
enabled: false
type: jaeger
endpoint: localhost:6831
collector: http://localhost:14268/api/traces
service: phoenix
asset:
path:
+2
View File
@@ -11,6 +11,8 @@ LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
org.label-schema.vendor="ownCloud GmbH" \
org.label-schema.schema-version="1.0"
EXPOSE 9100 9104
ENTRYPOINT ["/usr/bin/ocis-phoenix"]
CMD ["server"]
+2
View File
@@ -11,6 +11,8 @@ LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
org.label-schema.vendor="ownCloud GmbH" \
org.label-schema.schema-version="1.0"
EXPOSE 9100 9104
ENTRYPOINT ["/usr/bin/ocis-phoenix"]
CMD ["server"]
+2
View File
@@ -11,6 +11,8 @@ LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
org.label-schema.vendor="ownCloud GmbH" \
org.label-schema.schema-version="1.0"
EXPOSE 9100 9104
ENTRYPOINT ["/usr/bin/ocis-phoenix"]
CMD ["server"]
+1 -1
View File
@@ -14,5 +14,5 @@ disableKinds = ["taxonomy", "taxonomyTerm", "RSS", "sitemap"]
[params]
author = "ownCloud GmbH"
description = "Serve Phoenix for Reva/oCIS"
description = "Serve Phoenix for oCIS"
keywords = "reva, ocis"
+1 -1
View File
@@ -5,4 +5,4 @@ anchor: "about"
weight: 10
---
This service embeds [Phoenix](https://github.com/owncloud/phoenix) to provide a UI for the ownCloud Infinite Scale stack.
This service embeds [Phoenix](https://github.com/owncloud/phoenix) to provide a UI for ownCloud Infinite Scale.
+5 -8
View File
@@ -5,23 +5,20 @@ anchor: "building"
weight: 30
---
As this project is built with Go, so you need to install that first. The installation of Go is out of the scope of this document, please follow the official documentation for [Go](golang), to build this project you have to install Go >= v1.12. After the installation of the required tools you need to get the sources:
As this project is built with Go, so you need to install that first. The installation of Go is out of the scope of this document, please follow the official documentation for [Go](https://golang.org/doc/install), to build this project you have to install Go >= v1.12. After the installation of the required tools you need to get the sources:
{{< highlight txt >}}
git clone https://github.com/owncloud/ocis-hello.git
cd ocis-hello
git clone https://github.com/owncloud/ocis-phoenix.git
cd ocis-phoenix
{{< / highlight >}}
All required tool besides Go itself and make are bundled or getting automatically installed within the `GOPATH`. All commands to build this project are part of our `Makefile`.
## Backend
### Backend
{{< highlight txt >}}
make generate
make build
{{< / highlight >}}
The above commands will download a [Phoenix](phoenix) release and embed it into the binary. Finally you should have the binary within the `bin/` folder now, give it a try with `./bin/ocis-hello -h` to see all available options.
[golang]: https://golang.org/doc/install
[phoenix]: https://github.com/owncloud/phoenix
The above commands will download a [Phoenix](https://github.com/owncloud/phoenix) release and embed it into the binary. Finally you should have the binary within the `bin/` folder now, give it a try with `./bin/ocis-phoenix -h` to see all available options.
+70 -38
View File
@@ -5,27 +5,30 @@ anchor: "getting-started"
weight: 20
---
## Installation
### Installation
So far we are offering two different variants for the installation. You can choose between [Docker](docker) or pre-built binaries which are stored on our download mirrors and GitHub releases. Maybe we will also provide system packages for the major distributions later if we see the need for it.
So far we are offering two different variants for the installation. You can choose between [Docker](https://www.docker.com/) or pre-built binaries which are stored on our download mirrors and GitHub releases. Maybe we will also provide system packages for the major distributions later if we see the need for it.
### Docker
#### Docker
TBD
### Binaries
#### Binaries
TBD
## Configuration
### Configuration
We provide overall three different variants of configuration. The variant based on environment variables and commandline flags are split up into global values and command-specific values.
### Envrionment variables
#### Envrionment variables
If you prefer to configure the service with environment variables you can see the available variables below.
#### Global
##### Global
PHOENIX_CONFIG_FILE
: Path to config file, empty default value
PHOENIX_LOG_LEVEL
: Set logging level, defaults to `info`
@@ -36,10 +39,25 @@ PHOENIX_LOG_COLOR
PHOENIX_LOG_PRETTY
: Enable pretty logging, defaults to `true`
#### Server
##### Server
PHOENIX_TRACING_ENABLED
: Enable sending traces, defaults to `false`
PHOENIX_TRACING_TYPE
: Tracing backend type, defaults to `jaeger`
PHOENIX_TRACING_ENDPOINT
: Endpoint for the agent, empty default value
PHOENIX_TRACING_COLLECTOR
: Endpoint for the collector, empty default value
PHOENIX_TRACING_SERVICE
: Service name for tracing, defaults to `phoenix`
PHOENIX_DEBUG_ADDR
: Address to bind debug server, defaults to `0.0.0.0:8090`
: Address to bind debug server, defaults to `0.0.0.0:9104`
PHOENIX_DEBUG_TOKEN
: Token to grant metrics access, empty default value
@@ -47,28 +65,31 @@ PHOENIX_DEBUG_TOKEN
PHOENIX_DEBUG_PPROF
: Enable pprof debugging, defaults to `false`
PHOENIX_HTTP_ADDR
: Address to bind http server, defaults to `0.0.0.0:8080`
PHOENIX_DEBUG_ZPAGES
: Enable zpages debugging, defaults to `false`
PHOENIX_HTTP_ROOT
: Root path for http endpoint, defaults to `/`
PHOENIX_HTTP_ADDR
: Address to bind http server, defaults to `0.0.0.0:9100`
PHOENIX_ASSET_PATH
: Path to custom assets, empty default value
PHOENIX_CONFIG_FILE
PHOENIX_WEB_CONFIG
: Path to phoenix config, empty default value
#### Health
##### Health
PHOENIX_DEBUG_ADDR
: Address to debug endpoint, defaults to `0.0.0.0:8090`
: Address to debug endpoint, defaults to `0.0.0.0:9104`
### Commandline flags
#### Commandline flags
If you prefer to configure the service with commandline flags you can see the available variables below.
#### Global
##### Global
--config-file
: Path to config file, empty default value
--log-level
: Set logging level, defaults to `info`
@@ -79,10 +100,25 @@ If you prefer to configure the service with commandline flags you can see the av
--log-pretty
: Enable pretty logging, defaults to `true`
#### Server
##### Server
--tracing-enabled
: Enable sending traces, defaults to `false`
--tracing-type
: Tracing backend type, defaults to `jaeger`
--tracing-endpoint
: Endpoint for the agent, empty default value
--tracing-collector
: Endpoint for the collector, empty default value
--tracing-service
: Service name for tracing, defaults to `phoenix`
--debug-addr
: Address to bind debug server, defaults to `0.0.0.0:8090`
: Address to bind debug server, defaults to `0.0.0.0:9104`
--debug-token
: Token to grant metrics access, empty default value
@@ -90,32 +126,32 @@ If you prefer to configure the service with commandline flags you can see the av
--debug-pprof
: Enable pprof debugging, defaults to `false`
--http-addr
: Address to bind http server, defaults to `0.0.0.0:8080`
--debug-zpages
: Enable zpages debugging, defaults to `false`
--http-root
: Root path for http endpoint, defaults to `/`
--http-addr
: Address to bind http server, defaults to `0.0.0.0:9100`
--asset-path
: Path to custom assets, empty default value
--config-file
--web-config
: Path to phoenix config, empty default value
#### Health
##### Health
--debug-addr
: Address to debug endpoint, defaults to `0.0.0.0:8090`
: Address to debug endpoint, defaults to `0.0.0.0:9104`
### Configuration file
#### Configuration file
So far we support the file formats `JSON` and `YAML`, if you want to get a full example configuration just take a look at [our repository](repo), there you can always see the latest configuration format. These example configurations include all available options and the default values. The configuration file will be automatically loaded if it's placed at `/etc/ocis/phoenix.yml`, `${HOME}/.ocis/phoenix.yml` or `$(pwd)/config/phoenix.yml`.
So far we support the file formats `JSON` and `YAML`, if you want to get a full example configuration just take a look at [our repository](https://github.com/owncloud/ocis-phoenix/tree/master/config), there you can always see the latest configuration format. These example configurations include all available options and the default values. The configuration file will be automatically loaded if it's placed at `/etc/ocis/phoenix.yml`, `${HOME}/.ocis/phoenix.yml` or `$(pwd)/config/phoenix.yml`.
## Usage
### Usage
The program provides a few sub-commands on execution. The available configuration methods have already been mentioned above. Generally you can always see a formated help output if you execute the binary via `ocis-phoenix --help`.
### Server
#### Server
The server command is used to start the http and debug server on two addresses within a single process. The http server is serving the general webservice while the debug server is used for health check, readiness check and to server the metrics mentioned below. For further help please execute:
@@ -123,7 +159,7 @@ The server command is used to start the http and debug server on two addresses w
ocis-phoenix server --help
{{< / highlight >}}
### Health
#### Health
The health command is used to execute a health check, if the exit code equals zero the service should be up and running, if the exist code is greater than zero the service is not in a healthy state. Generally this command is used within our Docker containers, it could also be used within Kubernetes.
@@ -131,9 +167,9 @@ The health command is used to execute a health check, if the exit code equals ze
ocis-phoenix health --help
{{< / highlight >}}
## Metrics
### Metrics
This service provides some [Prometheus](prom) metrics through the debug endpoint, you can optionally secure the metrics endpoint by some random token, which got to be configured through one of the flag `--debug-token` or the environment variable `PHOENIX_DEBUG_TOKEN` mentioned above. By default the metrics endpoint is bound to `http://0.0.0.0:8090/metrics`.
This service provides some [Prometheus](https://prometheus.io/) metrics through the debug endpoint, you can optionally secure the metrics endpoint by some random token, which got to be configured through one of the flag `--debug-token` or the environment variable `PHOENIX_DEBUG_TOKEN` mentioned above. By default the metrics endpoint is bound to `http://0.0.0.0:9104/metrics`.
go_gc_duration_seconds
: A summary of the GC invocation durations
@@ -230,7 +266,3 @@ promhttp_metric_handler_requests_in_flight
promhttp_metric_handler_requests_total
: Total number of scrapes by HTTP status code
[docker]: https://www.docker.com/
[repo]: https://github.com/owncloud/ocis-phoenix/tree/master/config
[prom]: https://prometheus.io/
+1 -3
View File
@@ -5,6 +5,4 @@ anchor: "license"
weight: 40
---
This project is licensed under the [Apache 2.0](license) license. For the license of the used libraries you have to check the respective sources.
[license]: https://github.com/owncloud/ocis-phoenix/blob/master/LICENSE
This project is licensed under the [Apache 2.0](https://github.com/owncloud/ocis-phoenix/blob/master/LICENSE) license. For the license of the used libraries you have to check the respective sources.
+14 -11
View File
@@ -1,18 +1,21 @@
module github.com/owncloud/ocis-phoenix
go 1.12
go 1.13
require (
github.com/UnnoTed/fileb0x v1.1.3 // indirect
contrib.go.opencensus.io/exporter/jaeger v0.2.0
contrib.go.opencensus.io/exporter/ocagent v0.6.0
contrib.go.opencensus.io/exporter/zipkin v0.1.1
github.com/UnnoTed/fileb0x v1.1.4 // indirect
github.com/cespare/reflex v0.2.0 // indirect
github.com/go-chi/chi v4.0.2+incompatible
github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5 // indirect
github.com/mitchellh/gox v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/micro/cli v0.2.0
github.com/ogier/pflag v0.0.1 // indirect
github.com/oklog/run v1.0.0
github.com/prometheus/client_golang v1.1.0
github.com/rs/zerolog v1.15.0
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.4.0
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
github.com/openzipkin/zipkin-go v0.2.2
github.com/owncloud/ocis-pkg v1.1.0
github.com/spf13/viper v1.5.0
go.opencensus.io v0.22.2
golang.org/x/net v0.0.0-20191206103017-1ddd1de85cb0
)
+782 -50
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -5,22 +5,24 @@ import (
"os"
"path"
"github.com/rs/zerolog/log"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-pkg/log"
)
//go:generate go run github.com/UnnoTed/fileb0x embed.yml
// assets gets initialized by New and provides the handler.
type assets struct {
path string
logger log.Logger
config *config.Config
}
// Open just implements the HTTP filesystem interface.
func (a assets) Open(original string) (http.File, error) {
if a.path != "" {
if stat, err := os.Stat(a.path); err == nil && stat.IsDir() {
if a.config.Asset.Path != "" {
if stat, err := os.Stat(a.config.Asset.Path); err == nil && stat.IsDir() {
custom := path.Join(
a.path,
a.config.Asset.Path,
original,
)
@@ -34,7 +36,8 @@ func (a assets) Open(original string) (http.File, error) {
return f, nil
}
} else {
log.Warn().
a.logger.Warn().
Str("path", a.config.Asset.Path).
Msg("Assets directory doesn't exist")
}
}
@@ -49,11 +52,9 @@ func (a assets) Open(original string) (http.File, error) {
// New returns a new http filesystem to serve assets.
func New(opts ...Option) http.FileSystem {
a := new(assets)
options := newOptions(opts...)
for _, opt := range opts {
opt(a)
return assets{
config: options.Config,
}
return a
}
-9
View File
@@ -1,9 +0,0 @@
package assets
import (
"testing"
)
func TestAssets(t *testing.T) {
t.Skip()
}
+832
View File
File diff suppressed because one or more lines are too long
+35 -6
View File
@@ -1,11 +1,40 @@
package assets
// Option configures an assets option.
type Option func(*assets)
import (
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-pkg/log"
)
// WithPath returns an option to set custom assets path.
func WithPath(val string) Option {
return func(a *assets) {
a.path = val
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Config *config.Config
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val log.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
-18
View File
@@ -1,18 +0,0 @@
package command
import (
"github.com/spf13/viper"
)
// init defined the default options for viper.
func init() {
viper.SetDefault("debug.addr", "0.0.0.0:8090")
viper.SetDefault("debug.token", "")
viper.SetDefault("debug.pprof", false)
viper.SetDefault("http.addr", "0.0.0.0:8080")
viper.SetDefault("http.root", "/")
viper.SetDefault("asset.path", "")
viper.SetDefault("config.file", "")
}
+19 -24
View File
@@ -3,52 +3,47 @@ package command
import (
"fmt"
"net/http"
"os"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/micro/cli"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/flagset"
)
// Health is the entrypoint for the health command.
func Health() *cobra.Command {
cmd := &cobra.Command{
Use: "health",
Short: "Check health status",
Long: "",
Run: func(cmd *cobra.Command, args []string) {
func Health(cfg *config.Config) cli.Command {
return cli.Command{
Name: "health",
Usage: "Check health status",
Flags: flagset.HealthWithConfig(cfg),
Action: func(c *cli.Context) error {
logger := NewLogger(cfg)
resp, err := http.Get(
fmt.Sprintf(
"http://%s/healthz",
viper.GetString("debug.addr"),
cfg.Debug.Addr,
),
)
if err != nil {
log.Error().
logger.Fatal().
Err(err).
Msg("Failed to request health check")
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Error().
logger.Fatal().
Int("code", resp.StatusCode).
Msg("Health seems to be in bad state")
os.Exit(1)
}
os.Exit(0)
logger.Debug().
Int("code", resp.StatusCode).
Msg("Health got a good state")
return nil
},
}
cmd.Flags().String("debug-addr", "", "Address to debug endpoint")
viper.BindPFlag("debug.addr", cmd.Flags().Lookup("debug-addr"))
viper.BindEnv("debug.addr", "PHOENIX_DEBUG_ADDR")
return cmd
}
-9
View File
@@ -1,9 +0,0 @@
package command
import (
"testing"
)
func TestHealth(t *testing.T) {
t.Skip()
}
+84 -85
View File
@@ -4,101 +4,100 @@ import (
"os"
"strings"
"github.com/micro/cli"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/flagset"
"github.com/owncloud/ocis-phoenix/pkg/version"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/owncloud/ocis-pkg/log"
"github.com/spf13/viper"
)
// Root is the entry point for the ocis-phoenix command.
func Root() *cobra.Command {
cmd := &cobra.Command{
Use: "ocis-phoenix",
Short: "Reva service for phoenix",
Long: ``,
Version: version.String,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
setupLogger()
setupConfig()
// Execute is the entry point for the ocis-phoenix command.
func Execute() error {
cfg := config.New()
app := &cli.App{
Name: "ocis-phoenix",
Version: version.String,
Usage: "Serve Phoenix for oCIS",
Compiled: version.Compiled(),
Authors: []cli.Author{
{
Name: "ownCloud GmbH",
Email: "support@owncloud.com",
},
},
Flags: flagset.RootWithConfig(cfg),
Before: func(c *cli.Context) error {
logger := NewLogger(cfg)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.SetEnvPrefix("PHOENIX")
viper.AutomaticEnv()
if c.IsSet("config-file") {
viper.SetConfigFile(c.String("config-file"))
} else {
viper.SetConfigName("phoenix")
viper.AddConfigPath("/etc/ocis")
viper.AddConfigPath("$HOME/.ocis")
viper.AddConfigPath("./config")
}
if err := viper.ReadInConfig(); err != nil {
switch err.(type) {
case viper.ConfigFileNotFoundError:
logger.Info().
Msg("Continue without config")
case viper.UnsupportedConfigError:
logger.Fatal().
Err(err).
Msg("Unsupported config type")
default:
logger.Fatal().
Err(err).
Msg("Failed to read config")
}
}
if err := viper.Unmarshal(&cfg); err != nil {
logger.Fatal().
Err(err).
Msg("Failed to parse config")
}
return nil
},
Commands: []cli.Command{
Server(cfg),
Health(cfg),
},
}
cmd.PersistentFlags().String("log-level", "", "Set logging level")
viper.BindPFlag("log.level", cmd.PersistentFlags().Lookup("log-level"))
viper.SetDefault("log.level", "info")
viper.BindEnv("log.level", "PHOENIX_LOG_LEVEL")
cmd.PersistentFlags().Bool("log-pretty", false, "Enable pretty logging")
viper.BindPFlag("log.pretty", cmd.PersistentFlags().Lookup("log-pretty"))
viper.SetDefault("log.pretty", true)
viper.BindEnv("log.pretty", "PHOENIX_LOG_PRETTY")
cmd.PersistentFlags().Bool("log-color", false, "Enable colored logging")
viper.BindPFlag("log.color", cmd.PersistentFlags().Lookup("log-color"))
viper.SetDefault("log.color", true)
viper.BindEnv("log.color", "PHOENIX_LOG_COLOR")
cmd.AddCommand(Server())
cmd.AddCommand(Health())
return cmd
}
// setupLogger prepares the logger.
func setupLogger() {
switch strings.ToLower(viper.GetString("log.level")) {
case "panic":
zerolog.SetGlobalLevel(zerolog.PanicLevel)
case "fatal":
zerolog.SetGlobalLevel(zerolog.FatalLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
cli.HelpFlag = &cli.BoolFlag{
Name: "help,h",
Usage: "Show the help",
}
if viper.GetBool("log.pretty") {
log.Logger = log.Output(
zerolog.ConsoleWriter{
Out: os.Stderr,
NoColor: !viper.GetBool("log.color"),
},
)
cli.VersionFlag = &cli.BoolFlag{
Name: "version,v",
Usage: "Print the version",
}
return app.Run(os.Args)
}
// setupConfig prepares the config.
func setupConfig() {
viper.SetConfigName("phoenix")
viper.AddConfigPath("/etc/ocis")
viper.AddConfigPath("$HOME/.ocis")
viper.AddConfigPath("./config")
if err := viper.ReadInConfig(); err != nil {
switch err.(type) {
case viper.ConfigFileNotFoundError:
log.Debug().
Msg("Initializing without config file")
case viper.UnsupportedConfigError:
log.Fatal().
Msg("Unsupported config type")
default:
if e := log.Debug(); e.Enabled() {
log.Fatal().
Err(err).
Msg("Failed to read config")
} else {
log.Fatal().
Msg("Failed to read config")
}
}
}
// NewLogger initializes a service-specific logger instance.
func NewLogger(cfg *config.Config) log.Logger {
return log.NewLogger(
log.Name("phoenix"),
log.Level(cfg.Log.Level),
log.Pretty(cfg.Log.Pretty),
log.Color(cfg.Log.Color),
)
}
-9
View File
@@ -1,9 +0,0 @@
package command
import (
"testing"
)
func TestRoot(t *testing.T) {
t.Skip()
}
+157 -195
View File
@@ -2,198 +2,189 @@ package command
import (
"context"
"net"
"net/http"
"os"
"os/signal"
"strings"
"time"
"contrib.go.opencensus.io/exporter/jaeger"
"contrib.go.opencensus.io/exporter/ocagent"
"contrib.go.opencensus.io/exporter/zipkin"
"github.com/micro/cli"
"github.com/oklog/run"
"github.com/owncloud/ocis-phoenix/pkg/router/debug"
"github.com/owncloud/ocis-phoenix/pkg/router/server"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
openzipkin "github.com/openzipkin/zipkin-go"
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/flagset"
"github.com/owncloud/ocis-phoenix/pkg/metrics"
"github.com/owncloud/ocis-phoenix/pkg/server/debug"
"github.com/owncloud/ocis-phoenix/pkg/server/http"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
// Server is the entrypoint for the server command.
func Server() *cobra.Command {
cmd := &cobra.Command{
Use: "server",
Short: "Start integrated server",
Long: "",
RunE: func(cmd *cobra.Command, args []string) error {
var gr run.Group
func Server(cfg *config.Config) cli.Command {
return cli.Command{
Name: "server",
Usage: "Start integrated server",
Flags: flagset.ServerWithConfig(cfg),
Action: func(c *cli.Context) error {
logger := NewLogger(cfg)
if cfg.Tracing.Enabled {
switch t := cfg.Tracing.Type; t {
case "agent":
exporter, err := ocagent.NewExporter(
ocagent.WithReconnectionPeriod(5*time.Second),
ocagent.WithAddress(cfg.Tracing.Endpoint),
ocagent.WithServiceName(cfg.Tracing.Service),
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create agent tracing")
return err
}
trace.RegisterExporter(exporter)
view.RegisterExporter(exporter)
case "jaeger":
exporter, err := jaeger.NewExporter(
jaeger.Options{
AgentEndpoint: cfg.Tracing.Endpoint,
CollectorEndpoint: cfg.Tracing.Collector,
ServiceName: cfg.Tracing.Service,
},
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create jaeger tracing")
return err
}
trace.RegisterExporter(exporter)
case "zipkin":
endpoint, err := openzipkin.NewEndpoint(
cfg.Tracing.Service,
cfg.Tracing.Endpoint,
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create zipkin tracing")
return err
}
exporter := zipkin.NewExporter(
zipkinhttp.NewReporter(
cfg.Tracing.Collector,
),
endpoint,
)
trace.RegisterExporter(exporter)
default:
logger.Warn().
Str("type", t).
Msg("Unknown tracing backend")
}
trace.ApplyConfig(
trace.Config{
DefaultSampler: trace.AlwaysSample(),
},
)
} else {
logger.Debug().
Msg("Tracing is not enabled")
}
var (
gr = run.Group{}
ctx, cancel = context.WithCancel(context.Background())
metrics = metrics.New()
)
defer cancel()
{
server := &http.Server{
Addr: viper.GetString("debug.addr"),
Handler: debug.Router(
debug.WithToken(viper.GetString("debug.token")),
debug.WithPprof(viper.GetBool("debug.pprof")),
),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
server, err := http.Server(
http.Logger(logger),
http.Context(ctx),
http.Config(cfg),
http.Metrics(metrics),
)
if err != nil {
logger.Info().
Err(err).
Str("transport", "http").
Msg("Failed to initialize server")
return err
}
gr.Add(func() error {
log.Info().
Str("addr", viper.GetString("debug.addr")).
Msg("Starting debug server")
return server.Run()
}, func(_ error) {
logger.Info().
Str("transport", "http").
Msg("Shutting down server")
if strings.HasPrefix(viper.GetString("debug.addr"), "unix://") {
socket := strings.TrimPrefix(viper.GetString("debug.addr"), "unix://")
if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
log.Error().
Err(err).
Str("socket", socket).
Msg("Failed to remove existing debug socket")
return err
}
listener, err := net.ListenUnix(
"unix",
&net.UnixAddr{
Name: socket,
Net: "unix",
},
)
if err != nil {
log.Error().
Err(err).
Msg("Failed to initialize debug unix socket")
return err
}
if err = os.Chmod(socket, os.FileMode(0666)); err != nil {
log.Error().
Err(err).
Msg("Failed to change debug socket permissions")
return err
}
return server.Serve(listener)
}
return server.ListenAndServe()
}, func(reason error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Error().
Err(err).
Msg("Failed to shutdown debug server gracefully")
return
}
if strings.HasPrefix(viper.GetString("debug.addr"), "unix://") {
socket := strings.TrimPrefix(viper.GetString("debug.addr"), "unix://")
if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
log.Error().
Err(err).
Str("socket", socket).
Msg("Failed to remove debug server socket")
}
}
log.Info().
Err(reason).
Msg("Shutdown debug server gracefully")
cancel()
})
}
{
server := &http.Server{
Addr: viper.GetString("http.addr"),
Handler: server.Router(
server.WithRoot(viper.GetString("http.root")),
server.WithPath(viper.GetString("asset.path")),
server.WithConfig(viper.GetString("config.file")),
),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
server, err := debug.Server(
debug.Logger(logger),
debug.Context(ctx),
debug.Config(cfg),
)
if err != nil {
logger.Info().
Err(err).
Str("transport", "debug").
Msg("Failed to initialize server")
return err
}
gr.Add(func() error {
log.Info().
Str("addr", viper.GetString("http.addr")).
Msg("Starting http server")
if strings.HasPrefix(viper.GetString("http.addr"), "unix://") {
socket := strings.TrimPrefix(viper.GetString("http.addr"), "unix://")
if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
log.Error().
Err(err).
Str("socket", socket).
Msg("Failed to remove existing http socket")
return err
}
listener, err := net.ListenUnix(
"unix",
&net.UnixAddr{
Name: socket,
Net: "unix",
},
)
if err != nil {
log.Error().
Err(err).
Msg("Failed to initialize http unix socket")
return err
}
if err = os.Chmod(socket, os.FileMode(0666)); err != nil {
log.Error().
Err(err).
Msg("Failed to change http socket permissions")
return err
}
return server.Serve(listener)
}
return server.ListenAndServe()
}, func(reason error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
}, func(_ error) {
ctx, timeout := context.WithTimeout(ctx, 5*time.Second)
defer timeout()
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Error().
logger.Info().
Err(err).
Msg("Failed to shutdown http server gracefully")
return
Str("transport", "debug").
Msg("Failed to shutdown server")
} else {
logger.Info().
Str("transport", "debug").
Msg("Shutting down server")
}
if strings.HasPrefix(viper.GetString("http.addr"), "unix://") {
socket := strings.TrimPrefix(viper.GetString("http.addr"), "unix://")
if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
log.Error().
Err(err).
Str("socket", socket).
Msg("Failed to remove http server socket")
}
}
log.Info().
Err(reason).
Msg("Shutdown http server gracefully")
})
}
@@ -208,40 +199,11 @@ func Server() *cobra.Command {
return nil
}, func(err error) {
close(stop)
cancel()
})
}
return gr.Run()
},
}
cmd.Flags().String("debug-addr", "", "Address to bind debug server")
viper.BindPFlag("debug.addr", cmd.Flags().Lookup("debug-addr"))
viper.BindEnv("debug.addr", "PHOENIX_DEBUG_ADDR")
cmd.Flags().String("debug-token", "", "Token to grant metrics access")
viper.BindPFlag("debug.token", cmd.Flags().Lookup("debug-token"))
viper.BindEnv("debug.token", "PHOENIX_DEBUG_TOKEN")
cmd.Flags().Bool("debug-pprof", false, "Enable pprof debugging")
viper.BindPFlag("debug.pprof", cmd.Flags().Lookup("debug-pprof"))
viper.BindEnv("debug.pprof", "PHOENIX_DEBUG_PPROF")
cmd.Flags().String("http-addr", "", "Address to bind http server")
viper.BindPFlag("http.addr", cmd.Flags().Lookup("http-addr"))
viper.BindEnv("http.addr", "PHOENIX_HTTP_ADDR")
cmd.Flags().String("http-root", "", "Root path for http endpoint")
viper.BindPFlag("http.root", cmd.Flags().Lookup("http-root"))
viper.BindEnv("http.root", "PHOENIX_HTTP_ROOT")
cmd.Flags().String("asset-path", "", "Path to custom assets")
viper.BindPFlag("asset.path", cmd.Flags().Lookup("asset-path"))
viper.BindEnv("asset.path", "PHOENIX_ASSET_PATH")
cmd.Flags().String("config-file", "", "Path to phoenix config")
viper.BindPFlag("config.file", cmd.Flags().Lookup("config-file"))
viper.BindEnv("config.file", "PHOENIX_CONFIG_FILE")
return cmd
}
-9
View File
@@ -1,9 +0,0 @@
package command
import (
"testing"
)
func TestServer(t *testing.T) {
t.Skip()
}
+56
View File
@@ -0,0 +1,56 @@
package config
// Log defines the available logging configuration.
type Log struct {
Level string
Pretty bool
Color bool
}
// Debug defines the available debug configuration.
type Debug struct {
Addr string
Token string
Pprof bool
Zpages bool
}
// HTTP defines the available http configuration.
type HTTP struct {
Addr string
}
// Tracing defines the available tracing configuration.
type Tracing struct {
Enabled bool
Type string
Endpoint string
Collector string
Service string
}
// Asset defines the available asset configuration.
type Asset struct {
Path string
}
// Phoenix defines the available phoenic configuration.
type Phoenix struct {
Path string
}
// Config combines all available configuration parts.
type Config struct {
File string
Log Log
Debug Debug
HTTP HTTP
Tracing Tracing
Asset Asset
Phoenix Phoenix
}
// New initializes a new configuration with or without defaults.
func New() *Config {
return &Config{}
}
+138
View File
@@ -0,0 +1,138 @@
package flagset
import (
"github.com/micro/cli"
"github.com/owncloud/ocis-phoenix/pkg/config"
)
// RootWithConfig applies cfg to the root flagset
func RootWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "config-file",
Value: "",
Usage: "Path to config file",
EnvVar: "PHOENIX_CONFIG_FILE",
Destination: &cfg.File,
},
&cli.StringFlag{
Name: "log-level",
Value: "info",
Usage: "Set logging level",
EnvVar: "PHOENIX_LOG_LEVEL",
Destination: &cfg.Log.Level,
},
&cli.BoolTFlag{
Name: "log-pretty",
Usage: "Enable pretty logging",
EnvVar: "PHOENIX_LOG_PRETTY",
Destination: &cfg.Log.Pretty,
},
&cli.BoolTFlag{
Name: "log-color",
Usage: "Enable colored logging",
EnvVar: "PHOENIX_LOG_COLOR",
Destination: &cfg.Log.Color,
},
}
}
// HealthWithConfig applies cfg to the root flagset
func HealthWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "debug-addr",
Value: "0.0.0.0:9104",
Usage: "Address to debug endpoint",
EnvVar: "PHOENIX_DEBUG_ADDR",
Destination: &cfg.Debug.Addr,
},
}
}
// ServerWithConfig applies cfg to the root flagset
func ServerWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "tracing-enabled",
Usage: "Enable sending traces",
EnvVar: "PHOENIX_TRACING_ENABLED",
Destination: &cfg.Tracing.Enabled,
},
&cli.StringFlag{
Name: "tracing-type",
Value: "jaeger",
Usage: "Tracing backend type",
EnvVar: "PHOENIX_TRACING_TYPE",
Destination: &cfg.Tracing.Type,
},
&cli.StringFlag{
Name: "tracing-endpoint",
Value: "",
Usage: "Endpoint for the agent",
EnvVar: "PHOENIX_TRACING_ENDPOINT",
Destination: &cfg.Tracing.Endpoint,
},
&cli.StringFlag{
Name: "tracing-collector",
Value: "",
Usage: "Endpoint for the collector",
EnvVar: "PHOENIX_TRACING_COLLECTOR",
Destination: &cfg.Tracing.Collector,
},
&cli.StringFlag{
Name: "tracing-service",
Value: "phoenix",
Usage: "Service name for tracing",
EnvVar: "PHOENIX_TRACING_SERVICE",
Destination: &cfg.Tracing.Service,
},
&cli.StringFlag{
Name: "debug-addr",
Value: "0.0.0.0:9104",
Usage: "Address to bind debug server",
EnvVar: "PHOENIX_DEBUG_ADDR",
Destination: &cfg.Debug.Addr,
},
&cli.StringFlag{
Name: "debug-token",
Value: "",
Usage: "Token to grant metrics access",
EnvVar: "PHOENIX_DEBUG_TOKEN",
Destination: &cfg.Debug.Token,
},
&cli.BoolFlag{
Name: "debug-pprof",
Usage: "Enable pprof debugging",
EnvVar: "PHOENIX_DEBUG_PPROF",
Destination: &cfg.Debug.Pprof,
},
&cli.BoolFlag{
Name: "debug-zpages",
Usage: "Enable zpages debugging",
EnvVar: "PHOENIX_DEBUG_ZPAGES",
Destination: &cfg.Debug.Zpages,
},
&cli.StringFlag{
Name: "http-addr",
Value: "0.0.0.0:9100",
Usage: "Address to bind http server",
EnvVar: "PHOENIX_HTTP_ADDR",
Destination: &cfg.HTTP.Addr,
},
&cli.StringFlag{
Name: "asset-path",
Value: "",
Usage: "Path to custom assets",
EnvVar: "PHOENIX_ASSET_PATH",
Destination: &cfg.Asset.Path,
},
&cli.StringFlag{
Name: "web-config",
Value: "",
Usage: "Path to phoenix config",
EnvVar: "PHOENIX_WEB_CONFIG",
Destination: &cfg.Phoenix.Path,
},
}
}
-59
View File
@@ -1,59 +0,0 @@
package config
import (
"io/ioutil"
"net/http"
"os"
"github.com/rs/zerolog/log"
)
var (
// ErrConfigInvalid is returned when the config parse is invalid.
ErrConfigInvalid = `Invalid or missing config`
)
// config gets initialized by New and provides the handler.
type config struct {
file string
}
// ServeHTTP just implements the http.Handler interface.
func (c config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if _, err := os.Stat(c.file); os.IsNotExist(err) {
log.Error().
Err(err).
Str("config", c.file).
Msg("Phoenix config doesn't exist")
http.Error(w, ErrConfigInvalid, http.StatusUnprocessableEntity)
return
}
payload, err := ioutil.ReadFile(c.file)
if err != nil {
log.Error().
Err(err).
Str("config", c.file).
Msg("Failed to read custom config")
http.Error(w, ErrConfigInvalid, http.StatusUnprocessableEntity)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(payload)
}
// Handler returns the handler for config endpoint.
func Handler(opts ...Option) http.Handler {
c := new(config)
for _, opt := range opts {
opt(c)
}
return c
}
-9
View File
@@ -1,9 +0,0 @@
package config
import (
"testing"
)
func TestConfig(t *testing.T) {
t.Skip()
}
-11
View File
@@ -1,11 +0,0 @@
package config
// Option configures an assets option.
type Option func(*config)
// WithConfig returns an option to set config.
func WithConfig(val string) Option {
return func(c *config) {
c.file = val
}
}
-58
View File
@@ -1,58 +0,0 @@
package metrics
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
)
var (
// ErrInvalidToken is returned when the request token is invalid.
ErrInvalidToken = `Invalid or missing token`
)
// metrics gets initialized by New and provides the handler.
type metrics struct {
token string
}
// ServeHTTP just implements the http.Handler interface.
func (m metrics) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if m.token == "" {
promhttp.Handler().ServeHTTP(w, r)
return
}
header := r.Header.Get("Authorization")
if header == "" {
log.Debug().
Msg("Missing auth header")
http.Error(w, ErrInvalidToken, http.StatusUnauthorized)
return
}
if header != fmt.Sprintf("Bearer %s", m.token) {
log.Debug().
Msg("Invalid token provided")
http.Error(w, ErrInvalidToken, http.StatusUnauthorized)
return
}
promhttp.Handler().ServeHTTP(w, r)
}
// Handler returns the handler for metrics endpoint.
func Handler(opts ...Option) http.Handler {
m := new(metrics)
for _, opt := range opts {
opt(m)
}
return m
}
-9
View File
@@ -1,9 +0,0 @@
package metrics
import (
"testing"
)
func TestMetrics(t *testing.T) {
t.Skip()
}
-11
View File
@@ -1,11 +0,0 @@
package metrics
// Option configures an assets option.
type Option func(*metrics)
// WithToken returns an option to set a token.
func WithToken(val string) Option {
return func(m *metrics) {
m.token = val
}
}
-18
View File
@@ -1,18 +0,0 @@
package static
// Option configures an assets option.
type Option func(*static)
// WithRoot returns an option to set a root.
func WithRoot(val string) Option {
return func(s *static) {
s.root = val
}
}
// WithPath returns an option to set a path.
func WithPath(val string) Option {
return func(s *static) {
s.path = val
}
}
-40
View File
@@ -1,40 +0,0 @@
package static
import (
"net/http"
"github.com/owncloud/ocis-phoenix/pkg/assets"
)
// static gets initialized by New and provides the handler.
type static struct {
root string
path string
handler http.Handler
}
// ServeHTTP just implements the http.Handler interface.
func (s static) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.handler.ServeHTTP(w, r)
}
// Handler returns the handler for static endpoint.
func Handler(opts ...Option) http.Handler {
s := new(static)
for _, opt := range opts {
opt(s)
}
s.handler = http.StripPrefix(
s.root,
http.FileServer(
assets.New(
assets.WithPath(s.path),
),
),
)
return s
}
-9
View File
@@ -1,9 +0,0 @@
package static
import (
"testing"
)
func TestStatic(t *testing.T) {
t.Skip()
}
+32
View File
@@ -0,0 +1,32 @@
package metrics
var (
// Namespace defines the namespace for the defines metrics.
Namespace = "ocis"
// Subsystem defines the subsystem for the defines metrics.
Subsystem = "phoenix"
)
// Metrics defines the available metrics of this service.
type Metrics struct {
// Counter *prometheus.CounterVec
}
// New initializes the available metrics.
func New() *Metrics {
m := &Metrics{
// Counter: prometheus.NewCounterVec(prometheus.CounterOpts{
// Namespace: Namespace,
// Subsystem: Subsystem,
// Name: "greet_total",
// Help: "How many greeting requests processed",
// }, []string{}),
}
// prometheus.Register(
// m.Counter,
// )
return m
}
-60
View File
@@ -1,60 +0,0 @@
package header
import (
"net/http"
"time"
"github.com/owncloud/ocis-phoenix/pkg/version"
)
// Cache writes required cache headers to all requests.
func Cache(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
w.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
next.ServeHTTP(w, r)
})
}
// Options writes required option headers to all requests.
func Options(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "OPTIONS" {
next.ServeHTTP(w, r)
} else {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
w.Header().Set("Allow", "HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS")
w.WriteHeader(http.StatusOK)
}
})
}
// Secure writes required access headers to all requests.
func Secure(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
if r.TLS != nil {
w.Header().Set("Strict-Transport-Security", "max-age=31536000")
}
next.ServeHTTP(w, r)
})
}
// Version writes the current version to the headers.
func Version(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-PHOENIX-VERSION", version.String)
next.ServeHTTP(w, r)
})
}
-9
View File
@@ -1,9 +0,0 @@
package header
import (
"testing"
)
func TestHeader(t *testing.T) {
t.Skip()
}
-74
View File
@@ -1,74 +0,0 @@
package debug
import (
"io"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/owncloud/ocis-phoenix/pkg/handler/metrics"
"github.com/owncloud/ocis-phoenix/pkg/middleware/header"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)
// debug gets initialized by Router and configures the router.
type debug struct {
token string
pprof bool
}
// Router initializes a router for the debug server.
func Router(opts ...Option) *chi.Mux {
d := new(debug)
for _, opt := range opts {
opt(d)
}
mux := chi.NewRouter()
mux.Use(hlog.NewHandler(log.Logger))
mux.Use(hlog.RemoteAddrHandler("ip"))
mux.Use(hlog.URLHandler("path"))
mux.Use(hlog.MethodHandler("method"))
mux.Use(hlog.RequestIDHandler("request_id", "Request-Id"))
mux.Use(middleware.RealIP)
mux.Use(header.Version)
mux.Use(header.Cache)
mux.Use(header.Secure)
mux.Use(header.Options)
mux.Route("/", func(root chi.Router) {
if d.pprof {
root.Mount(
"/debug",
middleware.Profiler(),
)
}
root.Mount(
"/metrics",
metrics.Handler(
metrics.WithToken(d.token),
),
)
root.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
io.WriteString(w, http.StatusText(http.StatusOK))
})
root.Get("/readyz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
io.WriteString(w, http.StatusText(http.StatusOK))
})
})
return mux
}
-9
View File
@@ -1,9 +0,0 @@
package debug
import (
"testing"
)
func TestDebug(t *testing.T) {
t.Skip()
}
-18
View File
@@ -1,18 +0,0 @@
package debug
// Option configures an assets option.
type Option func(*debug)
// WithToken returns an option to set a token.
func WithToken(val string) Option {
return func(d *debug) {
d.token = val
}
}
// WithPprof returns an option to enable pprof.
func WithPprof(val bool) Option {
return func(d *debug) {
d.pprof = val
}
}
-25
View File
@@ -1,25 +0,0 @@
package server
// Option configures an assets option.
type Option func(*server)
// WithRoot returns an option to set root.
func WithRoot(val string) Option {
return func(s *server) {
s.root = val
}
}
// WithPath returns an option to set path.
func WithPath(val string) Option {
return func(s *server) {
s.path = val
}
}
// WithConfig returns an option to set config.
func WithConfig(val string) Option {
return func(s *server) {
s.config = val
}
}
-73
View File
@@ -1,73 +0,0 @@
package server
import (
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/owncloud/ocis-phoenix/pkg/handler/config"
"github.com/owncloud/ocis-phoenix/pkg/handler/static"
"github.com/owncloud/ocis-phoenix/pkg/middleware/header"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)
// server gets initialized by Router and configures the router.
type server struct {
root string
path string
config string
}
// Router initializes a router for the http server.
func Router(opts ...Option) *chi.Mux {
s := new(server)
for _, opt := range opts {
opt(s)
}
mux := chi.NewRouter()
mux.Use(hlog.NewHandler(log.Logger))
mux.Use(hlog.RemoteAddrHandler("ip"))
mux.Use(hlog.URLHandler("path"))
mux.Use(hlog.MethodHandler("method"))
mux.Use(hlog.RequestIDHandler("request_id", "Request-Id"))
mux.Use(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Debug().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))
mux.Use(middleware.RealIP)
mux.Use(header.Version)
mux.Use(header.Cache)
mux.Use(header.Secure)
mux.Use(header.Options)
mux.Route(s.root, func(root chi.Router) {
root.Mount(
"/config.json",
config.Handler(
config.WithConfig(s.config),
),
)
root.Mount(
"/",
static.Handler(
static.WithRoot(s.root),
static.WithPath(s.path),
),
)
})
return mux
}
-9
View File
@@ -1,9 +0,0 @@
package server
import (
"testing"
)
func TestServer(t *testing.T) {
t.Skip()
}
+50
View File
@@ -0,0 +1,50 @@
package debug
import (
"context"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-pkg/log"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Context context.Context
Config *config.Config
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val log.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Context provides a function to set the context option.
func Context(val context.Context) Option {
return func(o *Options) {
o.Context = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
+51
View File
@@ -0,0 +1,51 @@
package debug
import (
"io"
"net/http"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/version"
"github.com/owncloud/ocis-pkg/service/debug"
)
// Server initializes the debug service and server.
func Server(opts ...Option) (*http.Server, error) {
options := newOptions(opts...)
return debug.NewService(
debug.Logger(options.Logger),
debug.Name("phoenix"),
debug.Version(version.String),
debug.Address(options.Config.Debug.Addr),
debug.Token(options.Config.Debug.Token),
debug.Pprof(options.Config.Debug.Pprof),
debug.Zpages(options.Config.Debug.Zpages),
debug.Health(health(options.Config)),
debug.Ready(ready(options.Config)),
), nil
}
// health implements the health check.
func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
}
}
// ready implements the ready check.
func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
}
}
+59
View File
@@ -0,0 +1,59 @@
package http
import (
"context"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/metrics"
"github.com/owncloud/ocis-pkg/log"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Context context.Context
Config *config.Config
Metrics *metrics.Metrics
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val log.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Context provides a function to set the context option.
func Context(val context.Context) Option {
return func(o *Options) {
o.Context = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
// Metrics provides a function to set the metrics option.
func Metrics(val *metrics.Metrics) Option {
return func(o *Options) {
o.Metrics = val
}
}
+66
View File
@@ -0,0 +1,66 @@
package http
import (
"github.com/owncloud/ocis-phoenix/pkg/assets"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-phoenix/pkg/flagset"
"github.com/owncloud/ocis-phoenix/pkg/service/v0"
"github.com/owncloud/ocis-phoenix/pkg/version"
"github.com/owncloud/ocis-pkg/middleware"
"github.com/owncloud/ocis-pkg/service/http"
)
// Server initializes the http service and server.
func Server(opts ...Option) (http.Service, error) {
options := newOptions(opts...)
service := http.NewService(
http.Logger(options.Logger),
http.Namespace("go.micro.web"),
http.Name("phoenix"),
http.Version(version.String),
http.Address(options.Config.HTTP.Addr),
http.Context(options.Context),
http.Flags(flagset.RootWithConfig(config.New())...),
http.Flags(flagset.ServerWithConfig(config.New())...),
)
handle := svc.NewService(
svc.Logger(options.Logger),
svc.Config(options.Config),
svc.Middleware(
middleware.RealIP,
middleware.RequestID,
middleware.Cache,
middleware.Cors,
middleware.Secure,
middleware.Version(
"phoenix",
version.String,
),
middleware.Logger(
options.Logger,
),
middleware.Static(
assets.New(
assets.Logger(options.Logger),
assets.Config(options.Config),
),
),
),
)
{
handle = svc.NewInstrument(handle, options.Metrics)
handle = svc.NewLogging(handle, options.Logger)
handle = svc.NewTracing(handle)
}
service.Handle(
"/",
handle,
)
service.Init()
return service, nil
}
+30
View File
@@ -0,0 +1,30 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis-phoenix/pkg/metrics"
)
// NewInstrument returns a service that instruments metrics.
func NewInstrument(next Service, metrics *metrics.Metrics) Service {
return instrument{
next: next,
metrics: metrics,
}
}
type instrument struct {
next Service
metrics *metrics.Metrics
}
// ServeHTTP implements the Service interface.
func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
i.next.ServeHTTP(w, r)
}
// Config implements the Service interface.
func (i instrument) Config(w http.ResponseWriter, r *http.Request) {
i.next.Config(w, r)
}
+30
View File
@@ -0,0 +1,30 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis-pkg/log"
)
// NewLogging returns a service that logs messages.
func NewLogging(next Service, logger log.Logger) Service {
return logging{
next: next,
logger: logger,
}
}
type logging struct {
next Service
logger log.Logger
}
// ServeHTTP implements the Service interface.
func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
l.next.ServeHTTP(w, r)
}
// Config implements the Service interface.
func (l logging) Config(w http.ResponseWriter, r *http.Request) {
l.next.Config(w, r)
}
+50
View File
@@ -0,0 +1,50 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-pkg/log"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Config *config.Config
Middleware []func(http.Handler) http.Handler
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val log.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
// Middleware provides a function to set the middleware option.
func Middleware(val ...func(http.Handler) http.Handler) Option {
return func(o *Options) {
o.Middleware = val
}
}
+80
View File
@@ -0,0 +1,80 @@
package svc
import (
"io/ioutil"
"net/http"
"os"
"github.com/go-chi/chi"
"github.com/owncloud/ocis-phoenix/pkg/config"
"github.com/owncloud/ocis-pkg/log"
)
var (
// ErrConfigInvalid is returned when the config parse is invalid.
ErrConfigInvalid = `Invalid or missing config`
)
// Service defines the extension handlers.
type Service interface {
ServeHTTP(http.ResponseWriter, *http.Request)
Config(http.ResponseWriter, *http.Request)
}
// NewService returns a service implementation for Service.
func NewService(opts ...Option) Service {
options := newOptions(opts...)
m := chi.NewMux()
m.Use(options.Middleware...)
svc := Phoenix{
config: options.Config,
mux: m,
}
m.HandleFunc("/config.json", svc.Config)
return svc
}
// Phoenix defines implements the business logic for Service.
type Phoenix struct {
logger log.Logger
config *config.Config
mux *chi.Mux
}
// ServeHTTP implements the Service interface.
func (g Phoenix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
g.mux.ServeHTTP(w, r)
}
// Config implements the Service interface.
func (g Phoenix) Config(w http.ResponseWriter, r *http.Request) {
if _, err := os.Stat(g.config.Phoenix.Path); os.IsNotExist(err) {
g.logger.Error().
Err(err).
Str("config", g.config.Phoenix.Path).
Msg("Phoenix config doesn't exist")
http.Error(w, ErrConfigInvalid, http.StatusUnprocessableEntity)
return
}
payload, err := ioutil.ReadFile(g.config.Phoenix.Path)
if err != nil {
g.logger.Error().
Err(err).
Str("config", g.config.Phoenix.Path).
Msg("Failed to read custom config")
http.Error(w, ErrConfigInvalid, http.StatusUnprocessableEntity)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(payload)
}
+26
View File
@@ -0,0 +1,26 @@
package svc
import (
"net/http"
)
// NewTracing returns a service that instruments traces.
func NewTracing(next Service) Service {
return tracing{
next: next,
}
}
type tracing struct {
next Service
}
// ServeHTTP implements the Service interface.
func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.next.ServeHTTP(w, r)
}
// Config implements the Service interface.
func (t tracing) Config(w http.ResponseWriter, r *http.Request) {
t.next.Config(w, r)
}
+10
View File
@@ -1,5 +1,9 @@
package version
import (
"time"
)
var (
// String gets defined by the build system.
String = "0.0.0"
@@ -7,3 +11,9 @@ var (
// Date indicates the build date.
Date = "00000000"
)
// Compiled returns the compile time of this service.
func Compiled() time.Time {
t, _ := time.Parse("20060102", Date)
return t
}
+2
View File
@@ -0,0 +1,2 @@
# backend
-r '^(cmd|pkg)/.*\.go$' -R '^node_modules/' -s -- sh -c 'make bin/ocis-phoenix-debug && bin/ocis-phoenix-debug --log-level debug server --debug-pprof --debug-zpages --asset-path assets/'