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 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).
- 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
@ -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).
- 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).
- 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 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).
- 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).
- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski).

View file

@ -8,6 +8,7 @@ package main
import (
"context"
"flag"
"time"
"github.com/drone/drone-runtime/engine/docker"
@ -20,13 +21,20 @@ import (
"github.com/sirupsen/logrus"
"github.com/joho/godotenv"
_ "github.com/joho/godotenv/autoload"
)
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()
if err != nil {
logrus.WithError(err).Fatalln("invalid configuration")
logger := logrus.WithError(err)
logger.Fatalln("invalid configuration")
}
initLogging(config)

View file

@ -46,17 +46,17 @@ type (
Config struct {
License string `envconfig:"DRONE_LICENSE"`
Authn Authentication
Agent Agent
Cron Cron
Cloning Cloning
Database Database
Datadog Datadog
Docker Docker
HTTP HTTP
Jsonnet Jsonnet
Logging Logging
// Prometheus Prometheus
Authn Authentication
Agent Agent
Cron Cron
Cloning Cloning
Database Database
Datadog Datadog
Docker Docker
HTTP HTTP
Jsonnet Jsonnet
Logging Logging
Prometheus Prometheus
Proxy Proxy
Registration Registration
Registries Registries
@ -162,6 +162,11 @@ type (
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 struct {
Filter []string `envconfig:"DRONE_REPOSITORY_FILTER"`

View file

@ -18,6 +18,7 @@ import (
"net/http"
"github.com/drone/drone/cmd/drone-server/config"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api"
"github.com/drone/drone/handler/web"
"github.com/drone/drone/metric"
@ -31,12 +32,19 @@ import (
"github.com/unrolled/secure"
)
type (
healthzHandler http.Handler
metricsHandler http.Handler
rpcHandlerV1 http.Handler
rpcHandlerV2 http.Handler
)
// wire set for loading the server.
var serverSet = wire.NewSet(
manager.New,
metric.NewServer,
api.New,
web.New,
provideMetric,
provideRouter,
provideRPC,
provideRPC2,
@ -46,26 +54,34 @@ var serverSet = wire.NewSet(
// provideRouter is a Wire provider function that returns a
// 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.Mount("/metrics", metrics)
r.Mount("/api", api.Handler())
r.Mount("/rpc/v2", rpcv2)
r.Mount("/rpc", rpc)
r.Mount("/rpc", rpcv1)
r.Mount("/", web.Handler())
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
// handler that exposes the build manager to a remote agent.
func provideRPC(m manager.BuildManager, config config.Config) http.Handler {
return rpc.NewServer(m, config.RPC.Secret)
func provideRPC(m manager.BuildManager, config config.Config) rpcHandlerV1 {
v := rpc.NewServer(m, config.RPC.Secret)
return rpcHandlerV1(v)
}
// provideRPC2 is a Wire provider function that returns an rpc
// handler that exposes the build manager to a remote agent.
func provideRPC2(m manager.BuildManager, config config.Config) rpc2.Server {
return rpc2.NewServer(m, config.RPC.Secret)
func provideRPC2(m manager.BuildManager, config config.Config) rpcHandlerV2 {
v := rpc2.NewServer(m, config.RPC.Secret)
return rpcHandlerV2(v)
}
// 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/web"
"github.com/drone/drone/livelog"
"github.com/drone/drone/metric"
"github.com/drone/drone/operator/manager"
"github.com/drone/drone/pubsub"
"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)
handler := provideRPC(buildManager, config2)
rpc2Server := provideRPC2(buildManager, config2)
metricServer := metric.NewServer(session)
metricServer := provideMetric(session, config2)
mux := provideRouter(server, webServer, handler, rpc2Server, metricServer)
serverServer := provideServer(mux, config2)
mainApplication := newApplication(cronScheduler, datadog, runner, serverServer, userStore)

View file

@ -36,6 +36,7 @@ func HandleLast(
namespace = chi.URLParam(r, "owner")
name = chi.URLParam(r, "name")
ref = r.FormValue("ref")
branch = r.FormValue("branch")
)
repo, err := repos.FindName(r.Context(), namespace, name)
if err != nil {
@ -45,6 +46,9 @@ func HandleLast(
if ref == "" {
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)
if err != nil {
render.NotFound(w, err)

View file

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

View file

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

View file

@ -26,7 +26,7 @@ func TestHandleMetrics(t *testing.T) {
session := mock.NewMockSession(controller)
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 {
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.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 {
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) {
controller := gomock.NewController(t)
defer controller.Finish()
@ -64,7 +81,7 @@ func TestHandleMetrics_AccessDenied(t *testing.T) {
session := mock.NewMockSession(controller)
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 {
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
// the response body is empty and should be discarded.
if res.StatusCode == 204 {
if res.StatusCode == 204 || out == nil {
return nil
}

View file

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

View file

@ -4,18 +4,6 @@
// +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
import (
@ -55,7 +43,11 @@ func NewServer(manager manager.BuildManager, secret string) Server {
func authorization(token string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
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)
} else {
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 {
return nil
} else if err != nil {
logger.WithFields(
logrus.Fields{
"stage-id": p.ID,
"build-id": p.BuildID,
"repo-id": p.RepoID,
}).Warnln("runner: cannot ack stage")
logger.WithError(err).
WithFields(
logrus.Fields{
"stage-id": p.ID,
"build-id": p.BuildID,
"repo-id": p.RepoID,
}).Warnln("runner: cannot ack stage")
return err
}