fix regression with rpc v1 client

This commit is contained in:
Brad Rydzewski 2019-05-21 13:00:16 -07:00
commit b52456f652
14 changed files with 137 additions and 55 deletions

View File

@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). - endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). - endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). - endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- enable optional prometheus metrics guest access, by [@janberktold](https://github.com/janberktold)
### Fixed
- retrieve latest build by branch, by [@tboerger](https://github.com/tboerger).
### Fixed ### Fixed

View File

@ -8,6 +8,7 @@ package main
import ( import (
"context" "context"
"flag"
"time" "time"
"github.com/drone/drone-runtime/engine/docker" "github.com/drone/drone-runtime/engine/docker"
@ -20,13 +21,20 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/joho/godotenv"
_ "github.com/joho/godotenv/autoload" _ "github.com/joho/godotenv/autoload"
) )
func main() { func main() {
var envfile string
flag.StringVar(&envfile, "env-file", ".env", "Read in a file of environment variables")
flag.Parse()
godotenv.Load(envfile)
config, err := config.Environ() config, err := config.Environ()
if err != nil { if err != nil {
logrus.WithError(err).Fatalln("invalid configuration") logger := logrus.WithError(err)
logger.Fatalln("invalid configuration")
} }
initLogging(config) initLogging(config)

View File

@ -56,7 +56,7 @@ type (
HTTP HTTP HTTP HTTP
Jsonnet Jsonnet Jsonnet Jsonnet
Logging Logging Logging Logging
// Prometheus Prometheus Prometheus Prometheus
Proxy Proxy Proxy Proxy
Registration Registration Registration Registration
Registries Registries Registries Registries
@ -162,6 +162,11 @@ type (
Text bool `envconfig:"DRONE_LOGS_TEXT"` Text bool `envconfig:"DRONE_LOGS_TEXT"`
} }
// Prometheus provides the prometheus configuration.
Prometheus struct {
EnableAnonymousAccess bool `envconfig:"DRONE_PROMETHEUS_ANONYMOUS_ACCESS" default:"false"`
}
// Repository provides the repository configuration. // Repository provides the repository configuration.
Repository struct { Repository struct {
Filter []string `envconfig:"DRONE_REPOSITORY_FILTER"` Filter []string `envconfig:"DRONE_REPOSITORY_FILTER"`

View File

@ -18,6 +18,7 @@ import (
"net/http" "net/http"
"github.com/drone/drone/cmd/drone-server/config" "github.com/drone/drone/cmd/drone-server/config"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api" "github.com/drone/drone/handler/api"
"github.com/drone/drone/handler/web" "github.com/drone/drone/handler/web"
"github.com/drone/drone/metric" "github.com/drone/drone/metric"
@ -31,12 +32,19 @@ import (
"github.com/unrolled/secure" "github.com/unrolled/secure"
) )
type (
healthzHandler http.Handler
metricsHandler http.Handler
rpcHandlerV1 http.Handler
rpcHandlerV2 http.Handler
)
// wire set for loading the server. // wire set for loading the server.
var serverSet = wire.NewSet( var serverSet = wire.NewSet(
manager.New, manager.New,
metric.NewServer,
api.New, api.New,
web.New, web.New,
provideMetric,
provideRouter, provideRouter,
provideRPC, provideRPC,
provideRPC2, provideRPC2,
@ -46,26 +54,34 @@ var serverSet = wire.NewSet(
// provideRouter is a Wire provider function that returns a // provideRouter is a Wire provider function that returns a
// router that is serves the provided handlers. // router that is serves the provided handlers.
func provideRouter(api api.Server, web web.Server, rpc http.Handler, rpcv2 rpc2.Server, metrics *metric.Server) *chi.Mux { func provideRouter(api api.Server, web web.Server, rpcv1 rpcHandlerV1, rpcv2 rpcHandlerV2, metrics *metric.Server) *chi.Mux {
r := chi.NewRouter() r := chi.NewRouter()
r.Mount("/metrics", metrics) r.Mount("/metrics", metrics)
r.Mount("/api", api.Handler()) r.Mount("/api", api.Handler())
r.Mount("/rpc/v2", rpcv2) r.Mount("/rpc/v2", rpcv2)
r.Mount("/rpc", rpc) r.Mount("/rpc", rpcv1)
r.Mount("/", web.Handler()) r.Mount("/", web.Handler())
return r return r
} }
// provideMetric is a Wire provider function that returns the
// metrics server exposing metrics in prometheus format.
func provideMetric(session core.Session, config config.Config) *metric.Server {
return metric.NewServer(session, config.Prometheus.EnableAnonymousAccess)
}
// provideRPC is a Wire provider function that returns an rpc // provideRPC is a Wire provider function that returns an rpc
// handler that exposes the build manager to a remote agent. // handler that exposes the build manager to a remote agent.
func provideRPC(m manager.BuildManager, config config.Config) http.Handler { func provideRPC(m manager.BuildManager, config config.Config) rpcHandlerV1 {
return rpc.NewServer(m, config.RPC.Secret) v := rpc.NewServer(m, config.RPC.Secret)
return rpcHandlerV1(v)
} }
// provideRPC2 is a Wire provider function that returns an rpc // provideRPC2 is a Wire provider function that returns an rpc
// handler that exposes the build manager to a remote agent. // handler that exposes the build manager to a remote agent.
func provideRPC2(m manager.BuildManager, config config.Config) rpc2.Server { func provideRPC2(m manager.BuildManager, config config.Config) rpcHandlerV2 {
return rpc2.NewServer(m, config.RPC.Secret) v := rpc2.NewServer(m, config.RPC.Secret)
return rpcHandlerV2(v)
} }
// provideServer is a Wire provider function that returns an // provideServer is a Wire provider function that returns an

View File

@ -10,7 +10,6 @@ import (
"github.com/drone/drone/handler/api" "github.com/drone/drone/handler/api"
"github.com/drone/drone/handler/web" "github.com/drone/drone/handler/web"
"github.com/drone/drone/livelog" "github.com/drone/drone/livelog"
"github.com/drone/drone/metric"
"github.com/drone/drone/operator/manager" "github.com/drone/drone/operator/manager"
"github.com/drone/drone/pubsub" "github.com/drone/drone/pubsub"
"github.com/drone/drone/service/commit" "github.com/drone/drone/service/commit"
@ -94,7 +93,7 @@ func InitializeApplication(config2 config.Config) (application, error) {
webServer := web.New(admissionService, buildStore, client, hookParser, coreLicense, licenseService, middleware, repositoryStore, session, syncer, triggerer, userStore, userService, webhookSender, options, system) webServer := web.New(admissionService, buildStore, client, hookParser, coreLicense, licenseService, middleware, repositoryStore, session, syncer, triggerer, userStore, userService, webhookSender, options, system)
handler := provideRPC(buildManager, config2) handler := provideRPC(buildManager, config2)
rpc2Server := provideRPC2(buildManager, config2) rpc2Server := provideRPC2(buildManager, config2)
metricServer := metric.NewServer(session) metricServer := provideMetric(session, config2)
mux := provideRouter(server, webServer, handler, rpc2Server, metricServer) mux := provideRouter(server, webServer, handler, rpc2Server, metricServer)
serverServer := provideServer(mux, config2) serverServer := provideServer(mux, config2)
mainApplication := newApplication(cronScheduler, datadog, runner, serverServer, userStore) mainApplication := newApplication(cronScheduler, datadog, runner, serverServer, userStore)

View File

@ -36,6 +36,7 @@ func HandleLast(
namespace = chi.URLParam(r, "owner") namespace = chi.URLParam(r, "owner")
name = chi.URLParam(r, "name") name = chi.URLParam(r, "name")
ref = r.FormValue("ref") ref = r.FormValue("ref")
branch = r.FormValue("branch")
) )
repo, err := repos.FindName(r.Context(), namespace, name) repo, err := repos.FindName(r.Context(), namespace, name)
if err != nil { if err != nil {
@ -45,6 +46,9 @@ func HandleLast(
if ref == "" { if ref == "" {
ref = fmt.Sprintf("refs/heads/%s", repo.Branch) ref = fmt.Sprintf("refs/heads/%s", repo.Branch)
} }
if branch != "" {
ref = fmt.Sprintf("refs/heads/%s", branch)
}
build, err := builds.FindRef(r.Context(), repo.ID, ref) build, err := builds.FindRef(r.Context(), repo.ID, ref)
if err != nil { if err != nil {
render.NotFound(w, err) render.NotFound(w, err)

View File

@ -26,13 +26,15 @@ var errAccessDenied = errors.New("Access denied")
type Server struct { type Server struct {
metrics http.Handler metrics http.Handler
session core.Session session core.Session
anonymous bool
} }
// NewServer returns a new metrics server. // NewServer returns a new metrics server.
func NewServer(session core.Session) *Server { func NewServer(session core.Session, anonymous bool) *Server {
return &Server{ return &Server{
metrics: promhttp.Handler(), metrics: promhttp.Handler(),
session: session, session: session,
anonymous: anonymous,
} }
} }
@ -41,9 +43,9 @@ func NewServer(session core.Session) *Server {
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user, _ := s.session.Get(r) user, _ := s.session.Get(r)
switch { switch {
case user == nil: case !s.anonymous && user == nil:
http.Error(w, errInvalidToken.Error(), 401) http.Error(w, errInvalidToken.Error(), 401)
case !user.Admin && !user.Machine: case !s.anonymous && !user.Admin && !user.Machine:
http.Error(w, errAccessDenied.Error(), 403) http.Error(w, errAccessDenied.Error(), 403)
default: default:
s.metrics.ServeHTTP(w, r) s.metrics.ServeHTTP(w, r)

View File

@ -27,7 +27,7 @@ type Server struct {
} }
// NewServer returns a new metrics server. // NewServer returns a new metrics server.
func NewServer(session core.Session) *Server { func NewServer(session core.Session, anonymous bool) *Server {
return new(Server) return new(Server)
} }

View File

@ -26,7 +26,7 @@ func TestHandleMetrics(t *testing.T) {
session := mock.NewMockSession(controller) session := mock.NewMockSession(controller)
session.EXPECT().Get(r).Return(mockUser, nil) session.EXPECT().Get(r).Return(mockUser, nil)
NewServer(session).ServeHTTP(w, r) NewServer(session, false).ServeHTTP(w, r)
if got, want := w.Code, 200; got != want { if got, want := w.Code, 200; got != want {
t.Errorf("Want status code %d, got %d", want, got) t.Errorf("Want status code %d, got %d", want, got)
} }
@ -46,13 +46,30 @@ func TestHandleMetrics_NoSession(t *testing.T) {
session := mock.NewMockSession(controller) session := mock.NewMockSession(controller)
session.EXPECT().Get(r).Return(nil, nil) session.EXPECT().Get(r).Return(nil, nil)
NewServer(session).ServeHTTP(w, r) NewServer(session, false).ServeHTTP(w, r)
if got, want := w.Code, 401; got != want { if got, want := w.Code, 401; got != want {
t.Errorf("Want status code %d, got %d", want, got) t.Errorf("Want status code %d, got %d", want, got)
} }
} }
func TestHandleMetrics_NoSessionButAnonymousAccessEnabled(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
session := mock.NewMockSession(controller)
session.EXPECT().Get(r).Return(nil, nil)
NewServer(session, true).ServeHTTP(w, r)
if got, want := w.Code, 200; got != want {
t.Errorf("Want status code %d, got %d", want, got)
}
}
func TestHandleMetrics_AccessDenied(t *testing.T) { func TestHandleMetrics_AccessDenied(t *testing.T) {
controller := gomock.NewController(t) controller := gomock.NewController(t)
defer controller.Finish() defer controller.Finish()
@ -64,7 +81,7 @@ func TestHandleMetrics_AccessDenied(t *testing.T) {
session := mock.NewMockSession(controller) session := mock.NewMockSession(controller)
session.EXPECT().Get(r).Return(mockUser, nil) session.EXPECT().Get(r).Return(mockUser, nil)
NewServer(session).ServeHTTP(w, r) NewServer(session, false).ServeHTTP(w, r)
if got, want := w.Code, 403; got != want { if got, want := w.Code, 403; got != want {
t.Errorf("Want status code %d, got %d", want, got) t.Errorf("Want status code %d, got %d", want, got)
} }

View File

@ -261,7 +261,7 @@ func (s *Client) send(ctx context.Context, path string, in, out interface{}) err
// Check the response for a 204 no content. This indicates // Check the response for a 204 no content. This indicates
// the response body is empty and should be discarded. // the response body is empty and should be discarded.
if res.StatusCode == 204 { if res.StatusCode == 204 || out == nil {
return nil return nil
} }

View File

@ -69,7 +69,7 @@ func TestAccept(t *testing.T) {
client := NewClient("http://drone.company.com", "correct-horse-battery-staple") client := NewClient("http://drone.company.com", "correct-horse-battery-staple")
gock.InterceptClient(client.client.HTTPClient) gock.InterceptClient(client.client.HTTPClient)
err := client.Accept(noContext, 1, "localhost") _, err := client.Accept(noContext, 1, "localhost")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

View File

@ -4,18 +4,6 @@
// +build !oss // +build !oss
/*
/stage POST (request)
/stage/{stage}?machine= POST (accept, details)
/stage/{stage} PUT (beforeAll, afterAll)
/stage/{stage}/steps/{step} PUT (before, after)
/build/{build}/watch POST (watch)
/stage/{stage}/logs/batch POST (batch)
/stage/{stage}/logs/upload POST (upload)
*/
package rpc2 package rpc2
import ( import (
@ -55,7 +43,11 @@ func NewServer(manager manager.BuildManager, secret string) Server {
func authorization(token string) func(http.Handler) http.Handler { func authorization(token string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token == r.Header.Get("X-Drone-Token") { // prevents system administrators from accidentally
// exposing drone without credentials.
if token == "" {
w.WriteHeader(403)
} else if token == r.Header.Get("X-Drone-Token") {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
} else { } else {
w.WriteHeader(401) w.WriteHeader(401)

View File

@ -0,0 +1,33 @@
// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build oss
package rpc2
import (
"net/http"
"github.com/drone/drone/operator/manager"
)
// Server wraps the chi Router in a custom type for wire
// injection purposes.
type Server http.Handler
// NewServer returns a new rpc server that enables remote
// interaction with the build controller using the http transport.
func NewServer(manager manager.BuildManager, secret string) Server {
return Server(http.NotFoundHandler())
}

View File

@ -579,7 +579,8 @@ func (r *Runner) poll(ctx context.Context) error {
if err == db.ErrOptimisticLock { if err == db.ErrOptimisticLock {
return nil return nil
} else if err != nil { } else if err != nil {
logger.WithFields( logger.WithError(err).
WithFields(
logrus.Fields{ logrus.Fields{
"stage-id": p.ID, "stage-id": p.ID,
"build-id": p.BuildID, "build-id": p.BuildID,