| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package frontend implements HTTP server that handles requests to default | 5 // Package frontend implements HTTP server that handles requests to default |
| 6 // module. | 6 // module. |
| 7 // | 7 // |
| 8 // It stitches together all the code. | 8 // It stitches together all the code. |
| 9 package frontend | 9 package frontend |
| 10 | 10 |
| 11 import ( | 11 import ( |
| 12 "net/http" | 12 "net/http" |
| 13 "sync" | 13 "sync" |
| 14 | 14 |
| 15 "github.com/golang/protobuf/proto" | 15 "github.com/golang/protobuf/proto" |
| 16 "github.com/julienschmidt/httprouter" | |
| 17 "golang.org/x/net/context" | 16 "golang.org/x/net/context" |
| 18 "google.golang.org/grpc" | 17 "google.golang.org/grpc" |
| 19 "google.golang.org/grpc/codes" | 18 "google.golang.org/grpc/codes" |
| 20 | 19 |
| 21 "github.com/luci/gae/service/info" | 20 "github.com/luci/gae/service/info" |
| 22 "github.com/luci/luci-go/appengine/gaeauth/server" | 21 "github.com/luci/luci-go/appengine/gaeauth/server" |
| 23 "github.com/luci/luci-go/appengine/gaemiddleware" | 22 "github.com/luci/luci-go/appengine/gaemiddleware" |
| 24 "github.com/luci/luci-go/appengine/tsmon" | 23 "github.com/luci/luci-go/appengine/tsmon" |
| 25 "github.com/luci/luci-go/common/logging" | 24 "github.com/luci/luci-go/common/logging" |
| 26 "github.com/luci/luci-go/server/auth" | 25 "github.com/luci/luci-go/server/auth" |
| 27 "github.com/luci/luci-go/server/auth/machine" | 26 "github.com/luci/luci-go/server/auth/machine" |
| 28 "github.com/luci/luci-go/server/discovery" | 27 "github.com/luci/luci-go/server/discovery" |
| 29 "github.com/luci/luci-go/server/prpc" | 28 "github.com/luci/luci-go/server/prpc" |
| 29 "github.com/luci/luci-go/server/router" |
| 30 | 30 |
| 31 "github.com/luci/luci-go/common/api/tokenserver/admin/v1" | 31 "github.com/luci/luci-go/common/api/tokenserver/admin/v1" |
| 32 "github.com/luci/luci-go/common/api/tokenserver/identity/v1" | 32 "github.com/luci/luci-go/common/api/tokenserver/identity/v1" |
| 33 "github.com/luci/luci-go/common/api/tokenserver/minter/v1" | 33 "github.com/luci/luci-go/common/api/tokenserver/minter/v1" |
| 34 | 34 |
| 35 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/admin/certau
thorities" | 35 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/admin/certau
thorities" |
| 36 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/admin/servic
eaccounts" | 36 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/admin/servic
eaccounts" |
| 37 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/identity/ide
ntityfetcher" | 37 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/identity/ide
ntityfetcher" |
| 38 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/minter/token
minter" | 38 "github.com/luci/luci-go/appengine/cmd/tokenserver/services/minter/token
minter" |
| 39 ) | 39 ) |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 case err != nil: | 75 case err != nil: |
| 76 return nil, grpc.Errorf(codes.Internal, "can't check ACL
- %s", err) | 76 return nil, grpc.Errorf(codes.Internal, "can't check ACL
- %s", err) |
| 77 case !admin: | 77 case !admin: |
| 78 return nil, grpc.Errorf(codes.PermissionDenied, "not an
admin") | 78 return nil, grpc.Errorf(codes.PermissionDenied, "not an
admin") |
| 79 } | 79 } |
| 80 return c, nil | 80 return c, nil |
| 81 } | 81 } |
| 82 } | 82 } |
| 83 | 83 |
| 84 func init() { | 84 func init() { |
| 85 » router := httprouter.New() | 85 » r := router.New() |
| 86 » base := gaemiddleware.BaseProd | 86 » basemw := gaemiddleware.BaseProd() |
| 87 | 87 |
| 88 // Install auth, config and tsmon handlers. | 88 // Install auth, config and tsmon handlers. |
| 89 » gaemiddleware.InstallHandlers(router, base) | 89 » gaemiddleware.InstallHandlers(r, basemw) |
| 90 | 90 |
| 91 // The service has no UI, so just redirect to stock RPC explorer. | 91 // The service has no UI, so just redirect to stock RPC explorer. |
| 92 » router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httproute
r.Params) { | 92 » r.GET("/", nil, func(c *router.Context) { |
| 93 » » http.Redirect(w, r, "/rpcexplorer/", http.StatusFound) | 93 » » http.Redirect(c.Writer, c.Request, "/rpcexplorer/", http.StatusF
ound) |
| 94 }) | 94 }) |
| 95 | 95 |
| 96 // Optional warmup routes. | 96 // Optional warmup routes. |
| 97 » router.GET("/_ah/warmup", base(warmupHandler)) | 97 » r.GET("/_ah/warmup", basemw, warmupHandler) |
| 98 » router.GET("/_ah/start", base(warmupHandler)) | 98 » r.GET("/_ah/start", basemw, warmupHandler) |
| 99 | 99 |
| 100 // Backend routes used for cron and task queues. | 100 // Backend routes used for cron and task queues. |
| 101 » router.GET("/internal/cron/read-config", base(gaemiddleware.RequireCron(
readConfigCron))) | 101 » r.GET("/internal/cron/read-config", append(basemw, gaemiddleware.Require
Cron), readConfigCron) |
| 102 » router.GET("/internal/cron/fetch-crl", base(gaemiddleware.RequireCron(fe
tchCRLCron))) | 102 » r.GET("/internal/cron/fetch-crl", append(basemw, gaemiddleware.RequireCr
on), fetchCRLCron) |
| 103 | 103 |
| 104 // Install all RPC servers. | 104 // Install all RPC servers. |
| 105 api := prpc.Server{ | 105 api := prpc.Server{ |
| 106 Authenticator: auth.Authenticator{ | 106 Authenticator: auth.Authenticator{ |
| 107 &server.OAuth2Method{Scopes: []string{server.EmailScope}
}, | 107 &server.OAuth2Method{Scopes: []string{server.EmailScope}
}, |
| 108 &machine.MachineTokenAuthMethod{}, | 108 &machine.MachineTokenAuthMethod{}, |
| 109 }, | 109 }, |
| 110 UnaryServerInterceptor: tsmon.NewGrpcUnaryInterceptor(nil), | 110 UnaryServerInterceptor: tsmon.NewGrpcUnaryInterceptor(nil), |
| 111 } | 111 } |
| 112 admin.RegisterCertificateAuthoritiesServer(&api, caServerWithAuth) | 112 admin.RegisterCertificateAuthoritiesServer(&api, caServerWithAuth) |
| 113 admin.RegisterServiceAccountsServer(&api, serviceAccountsServerWithAuth) | 113 admin.RegisterServiceAccountsServer(&api, serviceAccountsServerWithAuth) |
| 114 identity.RegisterIdentityFetcherServer(&api, identityFetcher) | 114 identity.RegisterIdentityFetcherServer(&api, identityFetcher) |
| 115 minter.RegisterTokenMinterServer(&api, tokenMinterServerWithoutAuth) //
auth inside | 115 minter.RegisterTokenMinterServer(&api, tokenMinterServerWithoutAuth) //
auth inside |
| 116 discovery.Enable(&api) | 116 discovery.Enable(&api) |
| 117 » api.InstallHandlers(router, base) | 117 » api.InstallHandlers(r, basemw) |
| 118 | 118 |
| 119 // Expose all this stuff. | 119 // Expose all this stuff. |
| 120 » http.DefaultServeMux.Handle("/", router) | 120 » http.DefaultServeMux.Handle("/", r) |
| 121 } | 121 } |
| 122 | 122 |
| 123 /// Routes. | 123 /// Routes. |
| 124 | 124 |
| 125 // warmupHandler warms in-memory caches. | 125 // warmupHandler warms in-memory caches. |
| 126 func warmupHandler(c context.Context, w http.ResponseWriter, r *http.Request, _
httprouter.Params) { | 126 func warmupHandler(c *router.Context) { |
| 127 » if err := server.Warmup(c); err != nil { | 127 » if err := server.Warmup(c.Context); err != nil { |
| 128 panic(err) // let panic catcher deal with it | 128 panic(err) // let panic catcher deal with it |
| 129 } | 129 } |
| 130 » w.WriteHeader(http.StatusOK) | 130 » c.Writer.WriteHeader(http.StatusOK) |
| 131 } | 131 } |
| 132 | 132 |
| 133 // readConfigCron is handler for /internal/cron/read-config GAE cron task. | 133 // readConfigCron is handler for /internal/cron/read-config GAE cron task. |
| 134 func readConfigCron(c context.Context, w http.ResponseWriter, r *http.Request, _
httprouter.Params) { | 134 func readConfigCron(c *router.Context) { |
| 135 // Don't override manually imported configs with 'nil' on devserver. | 135 // Don't override manually imported configs with 'nil' on devserver. |
| 136 » if info.Get(c).IsDevAppServer() { | 136 » if info.Get(c.Context).IsDevAppServer() { |
| 137 » » w.WriteHeader(http.StatusOK) | 137 » » c.Writer.WriteHeader(http.StatusOK) |
| 138 return | 138 return |
| 139 } | 139 } |
| 140 » if _, err := caServerWithoutAuth.ImportConfig(c, nil); err != nil { | 140 » if _, err := caServerWithoutAuth.ImportConfig(c.Context, nil); err != ni
l { |
| 141 panic(err) // let panic catcher deal with it | 141 panic(err) // let panic catcher deal with it |
| 142 } | 142 } |
| 143 » w.WriteHeader(http.StatusOK) | 143 » c.Writer.WriteHeader(http.StatusOK) |
| 144 } | 144 } |
| 145 | 145 |
| 146 // fetchCRLCron is handler for /internal/cron/fetch-crl GAE cron task. | 146 // fetchCRLCron is handler for /internal/cron/fetch-crl GAE cron task. |
| 147 func fetchCRLCron(c context.Context, w http.ResponseWriter, r *http.Request, _ h
ttprouter.Params) { | 147 func fetchCRLCron(c *router.Context) { |
| 148 » list, err := caServerWithoutAuth.ListCAs(c, nil) | 148 » list, err := caServerWithoutAuth.ListCAs(c.Context, nil) |
| 149 if err != nil { | 149 if err != nil { |
| 150 panic(err) // let panic catcher deal with it | 150 panic(err) // let panic catcher deal with it |
| 151 } | 151 } |
| 152 | 152 |
| 153 // Fetch CRL of each active CA in parallel. In practice there are very f
ew | 153 // Fetch CRL of each active CA in parallel. In practice there are very f
ew |
| 154 // CAs there (~= 1), so the risk of OOM is small. | 154 // CAs there (~= 1), so the risk of OOM is small. |
| 155 wg := sync.WaitGroup{} | 155 wg := sync.WaitGroup{} |
| 156 errs := make([]error, len(list.Cn)) | 156 errs := make([]error, len(list.Cn)) |
| 157 for i, cn := range list.Cn { | 157 for i, cn := range list.Cn { |
| 158 wg.Add(1) | 158 wg.Add(1) |
| 159 go func(i int, cn string) { | 159 go func(i int, cn string) { |
| 160 defer wg.Done() | 160 defer wg.Done() |
| 161 » » » _, err := caServerWithoutAuth.FetchCRL(c, &admin.FetchCR
LRequest{Cn: cn}) | 161 » » » _, err := caServerWithoutAuth.FetchCRL(c.Context, &admin
.FetchCRLRequest{Cn: cn}) |
| 162 if err != nil { | 162 if err != nil { |
| 163 » » » » logging.Errorf(c, "FetchCRL(%q) failed - %s", cn
, err) | 163 » » » » logging.Errorf(c.Context, "FetchCRL(%q) failed -
%s", cn, err) |
| 164 errs[i] = err | 164 errs[i] = err |
| 165 } | 165 } |
| 166 }(i, cn) | 166 }(i, cn) |
| 167 } | 167 } |
| 168 wg.Wait() | 168 wg.Wait() |
| 169 | 169 |
| 170 // Retry cron job only on transient errors. On fatal errors let it rerun
one | 170 // Retry cron job only on transient errors. On fatal errors let it rerun
one |
| 171 // minute later, as usual, to avoid spamming logs with errors. | 171 // minute later, as usual, to avoid spamming logs with errors. |
| 172 status := http.StatusOK | 172 status := http.StatusOK |
| 173 for _, err = range errs { | 173 for _, err = range errs { |
| 174 if grpc.Code(err) == codes.Internal { | 174 if grpc.Code(err) == codes.Internal { |
| 175 status = http.StatusInternalServerError | 175 status = http.StatusInternalServerError |
| 176 break | 176 break |
| 177 } | 177 } |
| 178 } | 178 } |
| 179 » w.WriteHeader(status) | 179 » c.Writer.WriteHeader(status) |
| 180 } | 180 } |
| OLD | NEW |