diff --git a/service/client_builder.go b/service/client_builder.go index 999c3d03..effda0d7 100644 --- a/service/client_builder.go +++ b/service/client_builder.go @@ -24,6 +24,13 @@ package service import driver "github.com/arangodb/go-driver" +type ConnectionType int + +const ( + ConnectionTypeDatabase ConnectionType = iota + ConnectionTypeAgency +) + // ClientBuilder is a callback used to create authenticated go-driver clients with or without // follow-redirect. -type ClientBuilder func(endpoints []string, followRedirect bool) (driver.Client, error) +type ClientBuilder func(endpoints []string, connectionType ConnectionType) (driver.Client, error) diff --git a/service/cluster_config.go b/service/cluster_config.go index cf723e18..3338db49 100644 --- a/service/cluster_config.go +++ b/service/cluster_config.go @@ -283,11 +283,12 @@ func (p ClusterConfig) CreateAgencyAPI(clientBuilder ClientBuilder) (agency.Agen if err != nil { return nil, maskAny(err) } - c, err := clientBuilder(endpoints, true) + c, err := clientBuilder(endpoints, ConnectionTypeAgency) if err != nil { return nil, maskAny(err) } - a, err := agency.NewAgency(c.Connection()) + conn := c.Connection() + a, err := agency.NewAgency(conn) if err != nil { return nil, maskAny(err) } @@ -301,7 +302,7 @@ func (p ClusterConfig) CreateClusterAPI(ctx context.Context, clientBuilder Clien if err != nil { return nil, maskAny(err) } - c, err := clientBuilder(endpoints, true) + c, err := clientBuilder(endpoints, ConnectionTypeDatabase) if err != nil { return nil, maskAny(err) } diff --git a/service/peer.go b/service/peer.go index 711d86dd..bfcfd708 100644 --- a/service/peer.go +++ b/service/peer.go @@ -102,7 +102,7 @@ func (p Peer) CreateDBServerAPI(clientBuilder ClientBuilder) (driver.Client, err port := p.Port + p.PortOffset + ServerType(ServerTypeDBServer).PortOffset() scheme := NewURLSchemes(p.IsSecure).Browser ep := fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(p.Address, strconv.Itoa(port))) - c, err := clientBuilder([]string{ep}, false) + c, err := clientBuilder([]string{ep}, ConnectionTypeDatabase) if err != nil { return nil, maskAny(err) } @@ -117,7 +117,7 @@ func (p Peer) CreateCoordinatorAPI(clientBuilder ClientBuilder) (driver.Client, port := p.Port + p.PortOffset + ServerType(ServerTypeCoordinator).PortOffset() scheme := NewURLSchemes(p.IsSecure).Browser ep := fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(p.Address, strconv.Itoa(port))) - c, err := clientBuilder([]string{ep}, false) + c, err := clientBuilder([]string{ep}, ConnectionTypeDatabase) if err != nil { return nil, maskAny(err) } diff --git a/service/runtime_cluster_manager.go b/service/runtime_cluster_manager.go index 0c3f4202..0a6941e2 100644 --- a/service/runtime_cluster_manager.go +++ b/service/runtime_cluster_manager.go @@ -62,7 +62,7 @@ type runtimeClusterManagerContext interface { ChangeState(newState State) // CreateClient returns go-driver client with authentication configured for the given endpoint. - CreateClient(endpoint []string, followRedirect bool) (driver.Client, error) + CreateClient(endpoint []string, connectionType ConnectionType) (driver.Client, error) // UpdateClusterConfig updates the current cluster configuration. UpdateClusterConfig(ClusterConfig) diff --git a/service/service.go b/service/service.go index 23408580..85b84c6d 100644 --- a/service/service.go +++ b/service/service.go @@ -40,6 +40,7 @@ import ( "github.com/arangodb-helper/arangodb/client" driver "github.com/arangodb/go-driver" + "github.com/arangodb/go-driver/agency" driver_http "github.com/arangodb/go-driver/http" "github.com/arangodb/go-driver/jwt" logging "github.com/op/go-logging" @@ -940,14 +941,24 @@ func (s *Service) PrepareDatabaseServerRequestFunc() func(*http.Request) error { } // CreateClient creates a go-driver client with authentication for the given endpoints. -func (s *Service) CreateClient(endpoints []string, followRedirect bool) (driver.Client, error) { - conn, err := driver_http.NewConnection(driver_http.ConnectionConfig{ +func (s *Service) CreateClient(endpoints []string, connectionType ConnectionType) (driver.Client, error) { + connConfig := driver_http.ConnectionConfig{ Endpoints: endpoints, - DontFollowRedirect: !followRedirect, + DontFollowRedirect: connectionType == ConnectionTypeAgency, TLSConfig: &tls.Config{ InsecureSkipVerify: true, }, - }) + } + var conn driver.Connection + var err error + switch connectionType { + case ConnectionTypeDatabase: + conn, err = driver_http.NewConnection(connConfig) + case ConnectionTypeAgency: + conn, err = agency.NewAgencyConnection(connConfig) + default: + return nil, maskAny(fmt.Errorf("Unknown ConnectionType: %d", connectionType)) + } if err != nil { return nil, maskAny(err) } diff --git a/service/upgrade_manager.go b/service/upgrade_manager.go index 5b40f100..bae5d08c 100644 --- a/service/upgrade_manager.go +++ b/service/upgrade_manager.go @@ -55,7 +55,7 @@ type UpgradeManagerContext interface { // ClusterConfig returns the current cluster configuration and the current peer ClusterConfig() (ClusterConfig, *Peer, ServiceMode) // CreateClient creates a go-driver client with authentication for the given endpoints. - CreateClient(endpoints []string, followRedirect bool) (driver.Client, error) + CreateClient(endpoints []string, connectionType ConnectionType) (driver.Client, error) // RestartServer triggers a restart of the server of the given type. RestartServer(serverType ServerType) error } @@ -368,7 +368,7 @@ func (m *upgradeManager) isAgencyHealth(ctx context.Context) error { // Build agency clients clients := make([]driver.Connection, 0, len(endpoints)) for _, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeAgency) if err != nil { return maskAny(err) } @@ -392,7 +392,7 @@ func (m *upgradeManager) areDBServersResponding(ctx context.Context) error { } // Check all for _, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeDatabase) if err != nil { return maskAny(err) } @@ -414,7 +414,7 @@ func (m *upgradeManager) areCoordinatorsResponding(ctx context.Context) error { } // Check all for _, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeDatabase) if err != nil { return maskAny(err) } @@ -436,7 +436,7 @@ func (m *upgradeManager) areSingleServersResponding(ctx context.Context) error { } // Check all for _, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeDatabase) if err != nil { return maskAny(err) } @@ -459,7 +459,7 @@ func (m *upgradeManager) isSuperVisionMaintenanceSupported(ctx context.Context) } // Check agents for _, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeAgency) if err != nil { return false, maskAny(err) } @@ -546,7 +546,7 @@ func (m *upgradeManager) ShowArangodServerVersions(ctx context.Context) (bool, e showGroup := func(serverType ServerType, endpoints []string) { groupVersions := make([]string, len(endpoints)) for i, ep := range endpoints { - c, err := m.upgradeManagerContext.CreateClient([]string{ep}, false) + c, err := m.upgradeManagerContext.CreateClient([]string{ep}, ConnectionTypeDatabase) if err != nil { groupVersions[i] = "?" continue diff --git a/vendor/github.com/arangodb/go-driver/agency/agency_connection.go b/vendor/github.com/arangodb/go-driver/agency/agency_connection.go index 743be914..1ff703fb 100644 --- a/vendor/github.com/arangodb/go-driver/agency/agency_connection.go +++ b/vendor/github.com/arangodb/go-driver/agency/agency_connection.go @@ -143,11 +143,13 @@ func (c *agencyConnection) doOnce(ctx context.Context, req driver.Request) (driv epReq := req.Clone() result, err := epConn.Do(ctx, epReq) if err == nil { - // Success - results <- result - // Cancel all other requests - cancel() - return + if err = isSuccess(result); err == nil { + // Success + results <- result + // Cancel all other requests + cancel() + return + } } // Check error if statusCode, ok := isArangoError(err); ok { @@ -160,6 +162,10 @@ func (c *agencyConnection) doOnce(ctx context.Context, req driver.Request) (driv return } } + // No permanent error. Are we the only endpoint? + if len(connections) == 1 { + errors <- driver.WithStack(err) + } // No permanent error, try next agent }(epConn) } @@ -180,6 +186,20 @@ func (c *agencyConnection) doOnce(ctx context.Context, req driver.Request) (driv return nil, false, driver.WithStack(fmt.Errorf("All %d servers responded with temporary failure", len(connections))) } +func isSuccess(resp driver.Response) error { + if resp == nil { + return driver.WithStack(fmt.Errorf("Response is nil")) + } + statusCode := resp.StatusCode() + if statusCode >= 200 && statusCode < 300 { + return nil + } + return driver.ArangoError{ + HasError: true, + Code: statusCode, + } +} + // isArangoError checks if the given error is (or is caused by) an ArangoError. // If so it returned the Code and true, otherwise it returns 0, false. func isArangoError(err error) (int, bool) { @@ -224,7 +244,7 @@ func (c *agencyConnection) UpdateEndpoints(endpoints []string) error { for i, ep := range endpoints { config := c.config config.Endpoints = []string{ep} - config.FailOnRedirect = true + config.DontFollowRedirect = true httpConn, err := http.NewConnection(config) if err != nil { return driver.WithStack(err)