mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-17 04:11:24 +01:00
Update the HTTP client according to the comments
This commit is contained in:
parent
c4dc95f4f9
commit
a736cb7b09
@ -24,7 +24,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/src/adminserver/systeminfo/imagestorage"
|
||||
httpclient "github.com/vmware/harbor/src/common/http/client"
|
||||
common_http "github.com/vmware/harbor/src/common/http"
|
||||
"github.com/vmware/harbor/src/common/http/modifier/auth"
|
||||
"github.com/vmware/harbor/src/common/utils"
|
||||
)
|
||||
|
||||
@ -43,22 +44,33 @@ type Client interface {
|
||||
}
|
||||
|
||||
// NewClient return an instance of Adminserver client
|
||||
func NewClient(baseURL string, c httpclient.Client) Client {
|
||||
func NewClient(baseURL string, cfg *Config) Client {
|
||||
baseURL = strings.TrimRight(baseURL, "/")
|
||||
if !strings.Contains(baseURL, "://") {
|
||||
baseURL = "http://" + baseURL
|
||||
}
|
||||
return &client{
|
||||
client := &client{
|
||||
baseURL: baseURL,
|
||||
client: c,
|
||||
}
|
||||
if cfg != nil {
|
||||
authorizer := auth.NewSecretAuthorizer(cfg.Secret)
|
||||
client.client = common_http.NewClient(nil, authorizer)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
type client struct {
|
||||
baseURL string
|
||||
client httpclient.Client
|
||||
client *common_http.Client
|
||||
}
|
||||
|
||||
// Config contains configurations needed for client
|
||||
type Config struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
// TODO refactor the codes with methods of common_http.Client
|
||||
|
||||
// do creates request and authorizes it if authorizer is not nil
|
||||
func (c *client) do(method, relativePath string, body io.Reader) (*http.Response, error) {
|
||||
url := c.baseURL + relativePath
|
||||
|
@ -16,7 +16,6 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -35,7 +34,7 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c = NewClient(server.URL, &http.Client{})
|
||||
c = NewClient(server.URL, &Config{})
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
101
src/common/http/client.go
Normal file
101
src/common/http/client.go
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/src/common/http/modifier"
|
||||
)
|
||||
|
||||
// Client wraps net/http.Client with modifiers, modifiers the request before sending it
|
||||
type Client struct {
|
||||
modifiers []modifier.Modifier
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewClient creates an instance of Client. Use net/http.Client as the default value
|
||||
// if c is nil.
|
||||
func NewClient(c *http.Client, modifiers ...modifier.Modifier) *Client {
|
||||
client := &Client{
|
||||
client: c,
|
||||
}
|
||||
if client.client == nil {
|
||||
client.client = &http.Client{}
|
||||
}
|
||||
if len(modifiers) > 0 {
|
||||
client.modifiers = modifiers
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// Do ...
|
||||
func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
for _, modifier := range c.modifiers {
|
||||
if err := modifier.Modify(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.client.Do(req)
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (c *Client) Get(url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Head ...
|
||||
func (c *Client) Head(url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodHead, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Post ...
|
||||
func (c *Client) Post(url, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodPost, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Put ...
|
||||
func (c *Client) Put(url, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodPut, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (c *Client) Delete(url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/src/common/http/client/auth"
|
||||
)
|
||||
|
||||
// Client defines the method that a HTTP client should implement
|
||||
type Client interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// AuthorizedClient authorizes the requests before sending them
|
||||
type AuthorizedClient struct {
|
||||
client *http.Client
|
||||
authorizer auth.Authorizer
|
||||
}
|
||||
|
||||
// NewAuthorizedClient returns an instance of the AuthorizedClient
|
||||
func NewAuthorizedClient(authorizer auth.Authorizer, client ...*http.Client) *AuthorizedClient {
|
||||
c := &AuthorizedClient{
|
||||
authorizer: authorizer,
|
||||
}
|
||||
if len(client) > 0 {
|
||||
c.client = client[0]
|
||||
}
|
||||
if c.client == nil {
|
||||
c.client = &http.Client{}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Do authorizes the request before sending it
|
||||
func (a *AuthorizedClient) Do(req *http.Request) (*http.Response, error) {
|
||||
if a.authorizer != nil {
|
||||
if err := a.authorizer.Authorize(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return a.client.Do(req)
|
||||
}
|
@ -17,57 +17,38 @@ package auth
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/src/common/http/modifier"
|
||||
)
|
||||
|
||||
const (
|
||||
secretCookieName = "secret"
|
||||
)
|
||||
|
||||
// Authorizer authorizes the requests
|
||||
type Authorizer interface {
|
||||
Authorize(*http.Request) error
|
||||
}
|
||||
|
||||
// CookieAuthorizer authorizes the requests by adding cookie specified by name and value
|
||||
type CookieAuthorizer struct {
|
||||
name string
|
||||
value string
|
||||
}
|
||||
|
||||
// NewCookieAuthorizer returns an instance of CookieAuthorizer
|
||||
func NewCookieAuthorizer(name, value string) *CookieAuthorizer {
|
||||
return &CookieAuthorizer{
|
||||
name: name,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// Authorize the request with the cookie
|
||||
func (c *CookieAuthorizer) Authorize(req *http.Request) error {
|
||||
if req == nil {
|
||||
return errors.New("the request is null")
|
||||
}
|
||||
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: c.name,
|
||||
Value: c.value,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// Authorizer is a kind of Modifier used to authorize the requests
|
||||
type Authorizer modifier.Modifier
|
||||
|
||||
// SecretAuthorizer authorizes the requests with the specified secret
|
||||
type SecretAuthorizer struct {
|
||||
authorizer *CookieAuthorizer
|
||||
secret string
|
||||
}
|
||||
|
||||
// NewSecretAuthorizer returns an instance of SecretAuthorizer
|
||||
func NewSecretAuthorizer(secret string) *SecretAuthorizer {
|
||||
return &SecretAuthorizer{
|
||||
authorizer: NewCookieAuthorizer(secretCookieName, secret),
|
||||
secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
// Authorize the request with the secret
|
||||
func (s *SecretAuthorizer) Authorize(req *http.Request) error {
|
||||
return s.authorizer.Authorize(req)
|
||||
// Modify the request by adding secret authentication information
|
||||
func (s *SecretAuthorizer) Modify(req *http.Request) error {
|
||||
if req == nil {
|
||||
return errors.New("the request is null")
|
||||
}
|
||||
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: secretCookieName,
|
||||
Value: s.secret,
|
||||
})
|
||||
return nil
|
||||
}
|
@ -21,34 +21,17 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAuthorizeOfCookieAuthorizer(t *testing.T) {
|
||||
name, value := "name", "value"
|
||||
authorizer := NewCookieAuthorizer(name, value)
|
||||
|
||||
// nil request
|
||||
require.NotNil(t, authorizer.Authorize(nil))
|
||||
|
||||
// valid request
|
||||
req, err := http.NewRequest("", "", nil)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, authorizer.Authorize(req))
|
||||
require.Equal(t, 1, len(req.Cookies()))
|
||||
v, err := req.Cookie(name)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, value, v.Value)
|
||||
}
|
||||
|
||||
func TestAuthorizeOfSecretAuthorizer(t *testing.T) {
|
||||
secret := "secret"
|
||||
authorizer := NewSecretAuthorizer(secret)
|
||||
|
||||
// nil request
|
||||
require.NotNil(t, authorizer.Authorize(nil))
|
||||
require.NotNil(t, authorizer.Modify(nil))
|
||||
|
||||
// valid request
|
||||
req, err := http.NewRequest("", "", nil)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, authorizer.Authorize(req))
|
||||
require.Nil(t, authorizer.Modify(req))
|
||||
require.Equal(t, 1, len(req.Cookies()))
|
||||
v, err := req.Cookie(secretCookieName)
|
||||
require.Nil(t, err)
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package registry
|
||||
package modifier
|
||||
|
||||
import (
|
||||
"net/http"
|
@ -23,9 +23,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/vmware/harbor/src/common/http/modifier"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/utils/registry"
|
||||
token_util "github.com/vmware/harbor/src/ui/service/token"
|
||||
)
|
||||
|
||||
@ -254,7 +254,7 @@ func ping(client *http.Client, endpoint string) (string, string, error) {
|
||||
// from token server and add it to the origin request
|
||||
// If customizedTokenService is set, the token request will be sent to it instead of the server get from authorizer
|
||||
func NewStandardTokenAuthorizer(client *http.Client, credential Credential,
|
||||
customizedTokenService ...string) registry.Modifier {
|
||||
customizedTokenService ...string) modifier.Modifier {
|
||||
generator := &standardTokenGenerator{
|
||||
credential: credential,
|
||||
client: client,
|
||||
@ -309,7 +309,7 @@ func (s *standardTokenGenerator) generate(scopes []*token.ResourceActions, endpo
|
||||
|
||||
// NewRawTokenAuthorizer returns a token authorizer which calls method to create
|
||||
// token directly
|
||||
func NewRawTokenAuthorizer(username, service string) registry.Modifier {
|
||||
func NewRawTokenAuthorizer(username, service string) modifier.Modifier {
|
||||
generator := &rawTokenGenerator{
|
||||
service: service,
|
||||
username: username,
|
||||
|
@ -17,17 +17,18 @@ package registry
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/src/common/http/modifier"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
// Transport holds information about base transport and modifiers
|
||||
type Transport struct {
|
||||
transport http.RoundTripper
|
||||
modifiers []Modifier
|
||||
modifiers []modifier.Modifier
|
||||
}
|
||||
|
||||
// NewTransport ...
|
||||
func NewTransport(transport http.RoundTripper, modifiers ...Modifier) *Transport {
|
||||
func NewTransport(transport http.RoundTripper, modifiers ...modifier.Modifier) *Transport {
|
||||
return &Transport{
|
||||
transport: transport,
|
||||
modifiers: modifiers,
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
commonhttp "github.com/vmware/harbor/src/common/http"
|
||||
"github.com/vmware/harbor/src/common/http/client"
|
||||
"github.com/vmware/harbor/src/common/http/modifier/auth"
|
||||
"github.com/vmware/harbor/src/jobservice/api"
|
||||
)
|
||||
|
||||
@ -33,21 +33,22 @@ type Client interface {
|
||||
// DefaultClient provides a default implement for the interface Client
|
||||
type DefaultClient struct {
|
||||
endpoint string
|
||||
client client.Client
|
||||
client *commonhttp.Client
|
||||
}
|
||||
|
||||
// Config contains configuration items needed for DefaultClient
|
||||
type Config struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
// NewDefaultClient returns an instance of DefaultClient
|
||||
func NewDefaultClient(endpoint string, client ...client.Client) *DefaultClient {
|
||||
func NewDefaultClient(endpoint string, cfg *Config) *DefaultClient {
|
||||
c := &DefaultClient{
|
||||
endpoint: endpoint,
|
||||
}
|
||||
|
||||
if len(client) > 0 {
|
||||
c.client = client[0]
|
||||
}
|
||||
|
||||
if c.client == nil {
|
||||
c.client = &http.Client{}
|
||||
if cfg != nil {
|
||||
c.client = commonhttp.NewClient(nil, auth.NewSecretAuthorizer(cfg.Secret))
|
||||
}
|
||||
|
||||
return c
|
||||
@ -62,12 +63,7 @@ func (d *DefaultClient) SubmitReplicationJob(replication *api.ReplicationReq) er
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, url, buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := d.client.Do(req)
|
||||
resp, err := d.client.Post(url, "application/json", buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestSubmitReplicationJob(t *testing.T) {
|
||||
client := NewDefaultClient(url)
|
||||
client := NewDefaultClient(url, &Config{})
|
||||
err := client.SubmitReplicationJob(&api.ReplicationReq{})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
@ -22,8 +22,6 @@ import (
|
||||
"github.com/vmware/harbor/src/adminserver/client"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
comcfg "github.com/vmware/harbor/src/common/config"
|
||||
httpclient "github.com/vmware/harbor/src/common/http/client"
|
||||
"github.com/vmware/harbor/src/common/http/client/auth"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
)
|
||||
@ -51,8 +49,10 @@ func Init() error {
|
||||
adminServerURL = "http://adminserver"
|
||||
}
|
||||
log.Infof("initializing client for adminserver %s ...", adminServerURL)
|
||||
authorizer := auth.NewSecretAuthorizer(UISecret())
|
||||
AdminserverClient = client.NewClient(adminServerURL, httpclient.NewAuthorizedClient(authorizer))
|
||||
cfg := &client.Config{
|
||||
Secret: UISecret(),
|
||||
}
|
||||
AdminserverClient = client.NewClient(adminServerURL, cfg)
|
||||
if err := AdminserverClient.Ping(); err != nil {
|
||||
return fmt.Errorf("failed to ping adminserver: %v", err)
|
||||
}
|
||||
|
@ -18,11 +18,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/src/common/http/client"
|
||||
"github.com/vmware/harbor/src/common/http/client/auth"
|
||||
common_models "github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/jobservice/api"
|
||||
"github.com/vmware/harbor/src/jobservice/client"
|
||||
"github.com/vmware/harbor/src/replication"
|
||||
"github.com/vmware/harbor/src/replication/models"
|
||||
"github.com/vmware/harbor/src/replication/policy"
|
||||
@ -85,8 +84,10 @@ func NewDefaultController(cfg ControllerConfig) *DefaultController {
|
||||
|
||||
// TODO read from configuration
|
||||
endpoint := "http://jobservice:8080"
|
||||
client := client.NewAuthorizedClient(auth.NewSecretAuthorizer(config.UISecret()))
|
||||
ctl.replicator = replicator.NewDefaultReplicator(endpoint, client)
|
||||
ctl.replicator = replicator.NewDefaultReplicator(endpoint,
|
||||
&client.Config{
|
||||
Secret: config.UISecret(),
|
||||
})
|
||||
|
||||
return ctl
|
||||
}
|
||||
|
@ -15,9 +15,8 @@
|
||||
package replicator
|
||||
|
||||
import (
|
||||
"github.com/vmware/harbor/src/common/http/client"
|
||||
"github.com/vmware/harbor/src/jobservice/api"
|
||||
jobserviceclient "github.com/vmware/harbor/src/jobservice/client"
|
||||
"github.com/vmware/harbor/src/jobservice/client"
|
||||
)
|
||||
|
||||
// Replicator submits the replication work to the jobservice
|
||||
@ -27,13 +26,13 @@ type Replicator interface {
|
||||
|
||||
// DefaultReplicator provides a default implement for Replicator
|
||||
type DefaultReplicator struct {
|
||||
client jobserviceclient.Client
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// NewDefaultReplicator returns an instance of DefaultReplicator
|
||||
func NewDefaultReplicator(endpoint string, client ...client.Client) *DefaultReplicator {
|
||||
func NewDefaultReplicator(endpoint string, cfg *client.Config) *DefaultReplicator {
|
||||
return &DefaultReplicator{
|
||||
client: jobserviceclient.NewDefaultClient(endpoint, client...),
|
||||
client: client.NewDefaultClient(endpoint, cfg),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/jobservice/api"
|
||||
"github.com/vmware/harbor/src/jobservice/client"
|
||||
)
|
||||
|
||||
type fakeJobserviceClient struct{}
|
||||
@ -28,7 +29,7 @@ func (f *fakeJobserviceClient) SubmitReplicationJob(replication *api.Replication
|
||||
}
|
||||
|
||||
func TestReplicate(t *testing.T) {
|
||||
replicator := NewDefaultReplicator("http://jobservice")
|
||||
replicator := NewDefaultReplicator("http://jobservice", &client.Config{})
|
||||
replicator.client = &fakeJobserviceClient{}
|
||||
assert.Nil(t, replicator.Replicate(&api.ReplicationReq{}))
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ import (
|
||||
"github.com/vmware/harbor/src/adminserver/client"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
comcfg "github.com/vmware/harbor/src/common/config"
|
||||
httpclient "github.com/vmware/harbor/src/common/http/client"
|
||||
"github.com/vmware/harbor/src/common/http/client/auth"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/secret"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
@ -74,8 +72,10 @@ func Init() error {
|
||||
// InitByURL Init configurations with given url
|
||||
func InitByURL(adminServerURL string) error {
|
||||
log.Infof("initializing client for adminserver %s ...", adminServerURL)
|
||||
authorizer := auth.NewSecretAuthorizer(UISecret())
|
||||
AdminserverClient = client.NewClient(adminServerURL, httpclient.NewAuthorizedClient(authorizer))
|
||||
cfg := &client.Config{
|
||||
Secret: UISecret(),
|
||||
}
|
||||
AdminserverClient = client.NewClient(adminServerURL, cfg)
|
||||
if err := AdminserverClient.Ping(); err != nil {
|
||||
return fmt.Errorf("failed to ping adminserver: %v", err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user