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
@ -21,8 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651). - specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651).
- support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622). - support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622).
- ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495). - ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495).
- support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). - support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). - support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski). - sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski).
- endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski). - endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski).
- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski). - support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski).

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

@ -46,17 +46,17 @@ type (
Config struct { Config struct {
License string `envconfig:"DRONE_LICENSE"` License string `envconfig:"DRONE_LICENSE"`
Authn Authentication Authn Authentication
Agent Agent Agent Agent
Cron Cron Cron Cron
Cloning Cloning Cloning Cloning
Database Database Database Database
Datadog Datadog Datadog Datadog
Docker Docker Docker Docker
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

@ -24,15 +24,17 @@ var errAccessDenied = errors.New("Access denied")
// Server is an http Metrics server. // Server is an http Metrics server.
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,12 +579,13 @@ 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).
logrus.Fields{ WithFields(
"stage-id": p.ID, logrus.Fields{
"build-id": p.BuildID, "stage-id": p.ID,
"repo-id": p.RepoID, "build-id": p.BuildID,
}).Warnln("runner: cannot ack stage") "repo-id": p.RepoID,
}).Warnln("runner: cannot ack stage")
return err return err
} }