summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--build_settings.cmake13
-rw-r--r--client/go/auth/auth.go36
-rw-r--r--client/go/auth/token.go10
-rw-r--r--client/go/cli/cli.go186
-rw-r--r--client/go/cmd/helpers.go23
-rw-r--r--client/go/cmd/login.go2
-rw-r--r--client/go/vespa/target.go14
-rw-r--r--client/go/vespa/target_test.go13
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java10
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java4
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ConfigSentinel.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java20
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java10
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java17
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConnectionPool.java4
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java2
-rw-r--r--configd/src/apps/sentinel/manager.cpp5
-rw-r--r--configdefinitions/src/vespa/sentinel.def3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java25
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java23
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ProxyResponse.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java43
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java58
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java33
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java23
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java66
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java65
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java26
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java52
-rw-r--r--default_build_settings.cmake7
-rw-r--r--dist/vespa.spec1
-rw-r--r--fastos/src/vespa/fastos/unix_thread.cpp7
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java2
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java15
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java21
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java59
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_test.cpp8
-rw-r--r--searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp10
-rw-r--r--searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp6
-rw-r--r--searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp2
-rw-r--r--searchcore/src/tests/proton/index/indexmanager_test.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp37
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.cpp54
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.h1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h12
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp12
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/thread_utils.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/threading_service_observer.h14
-rw-r--r--searchcorespi/src/vespa/searchcorespi/index/ithreadingservice.h5
-rw-r--r--searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp26
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/sequencedtaskexecutor.h1
-rw-r--r--storage/src/vespa/storage/distributor/node_supported_features_repo.cpp2
-rw-r--r--storage/src/vespa/storage/distributor/node_supported_features_repo.h2
-rw-r--r--storage/src/vespa/storage/persistence/processallhandler.cpp4
-rwxr-xr-xvespabase/src/rhel-prestart.sh55
-rwxr-xr-xvespamalloc/src/tests/thread/thread_test.sh4
-rw-r--r--zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt2
-rw-r--r--zookeeper-server/zookeeper-server-3.7.0/CMakeLists.txt2
74 files changed, 729 insertions, 544 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea469733695..863ce8dd9de 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -150,8 +150,7 @@ add_subdirectory(vespajlib)
add_subdirectory(vespalib)
add_subdirectory(vespalog)
if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND
- NOT DEFINED VESPA_USE_SANITIZER AND
- CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ NOT DEFINED VESPA_USE_SANITIZER)
add_subdirectory(vespamalloc)
endif()
add_subdirectory(vsm)
diff --git a/build_settings.cmake b/build_settings.cmake
index a0b9ce5160a..6c3740f1bce 100644
--- a/build_settings.cmake
+++ b/build_settings.cmake
@@ -35,6 +35,7 @@ endif()
# Warnings that are specific to C++ compilation
# Note: this is not a union of C_WARN_OPTS, since CMAKE_CXX_FLAGS already includes CMAKE_C_FLAGS, which in turn includes C_WARN_OPTS transitively
+set(VESPA_ATOMIC_LIB "atomic")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
set(CXX_SPECIFIC_WARN_OPTS "-Wnon-virtual-dtor -Wformat-security -Wno-overloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-delete-null-pointer-checks -fsized-deallocation")
@@ -43,7 +44,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" ST
set(VESPA_GCC_LIB "")
set(VESPA_STDCXX_FS_LIB "")
else()
- set(VESPA_ATOMIC_LIB "atomic")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0)
set(VESPA_GCC_LIB "gcc")
set(VESPA_STDCXX_FS_LIB "stdc++fs")
@@ -54,17 +54,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" ST
endif()
else()
set(CXX_SPECIFIC_WARN_OPTS "-Wnoexcept -Wsuggest-override -Wnon-virtual-dtor -Wformat-security")
- if(VESPA_OS_DISTRO_COMBINED STREQUAL "centos 8" OR
- (VESPA_OS_DISTRO STREQUAL "rocky" AND
- VESPA_OS_DISTRO_VERSION VERSION_GREATER_EQUAL "8" AND
- VESPA_OS_DISTRO_VERSION VERSION_LESS "9") OR
- (VESPA_OS_DISTRO STREQUAL "rhel" AND
- VESPA_OS_DISTRO_VERSION VERSION_GREATER_EQUAL "8" AND
- VESPA_OS_DISTRO_VERSION VERSION_LESS "9"))
- set(VESPA_ATOMIC_LIB "")
- else()
- set(VESPA_ATOMIC_LIB "atomic")
- endif()
set(VESPA_GCC_LIB "gcc")
set(VESPA_STDCXX_FS_LIB "stdc++fs")
endif()
diff --git a/client/go/auth/auth.go b/client/go/auth/auth.go
index 397e410924d..100af7ea1d2 100644
--- a/client/go/auth/auth.go
+++ b/client/go/auth/auth.go
@@ -4,7 +4,6 @@ package auth
import (
"context"
- "encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -15,7 +14,6 @@ import (
)
const (
- audiencePath = "/api/v2/"
waitThresholdInSeconds = 3
// SecretsNamespace namespace used to set/get values from the keychain
SecretsNamespace = "vespa-cli"
@@ -39,8 +37,6 @@ type SecretStore interface {
}
type Result struct {
- Tenant string
- Domain string
RefreshToken string
AccessToken string
ExpiresIn int64
@@ -114,17 +110,10 @@ func (a *Authenticator) Wait(ctx context.Context, state State) (Result, error) {
return Result{}, errors.New(res.ErrorDescription)
}
- ten, domain, err := parseTenant(res.AccessToken)
- if err != nil {
- return Result{}, fmt.Errorf("cannot parse tenant from the given access token: %w", err)
- }
-
return Result{
RefreshToken: res.RefreshToken,
AccessToken: res.AccessToken,
ExpiresIn: res.ExpiresIn,
- Tenant: ten,
- Domain: domain,
}, nil
}
}
@@ -148,28 +137,3 @@ func (a *Authenticator) getDeviceCode(ctx context.Context) (State, error) {
}
return res, nil
}
-
-func parseTenant(accessToken string) (tenant, domain string, err error) {
- parts := strings.Split(accessToken, ".")
- v, err := base64.RawURLEncoding.DecodeString(parts[1])
- if err != nil {
- return "", "", err
- }
- var payload struct {
- AUDs []string `json:"aud"`
- }
- if err := json.Unmarshal(v, &payload); err != nil {
- return "", "", err
- }
- for _, aud := range payload.AUDs {
- u, err := url.Parse(aud)
- if err != nil {
- return "", "", err
- }
- if u.Path == audiencePath {
- parts := strings.Split(u.Host, ".")
- return parts[0], u.Host, nil
- }
- }
- return "", "", fmt.Errorf("audience not found for %s", audiencePath)
-}
diff --git a/client/go/auth/token.go b/client/go/auth/token.go
index e9b90b8994e..120c2602bad 100644
--- a/client/go/auth/token.go
+++ b/client/go/auth/token.go
@@ -25,16 +25,16 @@ type TokenRetriever struct {
Client *http.Client
}
-// Delete deletes the given tenant from the secrets' storage.
-func (t *TokenRetriever) Delete(tenant string) error {
- return t.Secrets.Delete(SecretsNamespace, tenant)
+// Delete deletes the given system from the secrets' storage.
+func (t *TokenRetriever) Delete(system string) error {
+ return t.Secrets.Delete(SecretsNamespace, system)
}
// Refresh gets a new access token from the provided refresh token,
// The request is used the default client_id and endpoint for device authentication.
-func (t *TokenRetriever) Refresh(ctx context.Context, tenant string) (TokenResponse, error) {
+func (t *TokenRetriever) Refresh(ctx context.Context, system string) (TokenResponse, error) {
// get stored refresh token:
- refreshToken, err := t.Secrets.Get(SecretsNamespace, tenant)
+ refreshToken, err := t.Secrets.Get(SecretsNamespace, system)
if err != nil {
return TokenResponse{}, fmt.Errorf("cannot get the stored refresh token: %w", err)
}
diff --git a/client/go/cli/cli.go b/client/go/cli/cli.go
index e1dde387b89..22c40f195b4 100644
--- a/client/go/cli/cli.go
+++ b/client/go/cli/cli.go
@@ -7,9 +7,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "github.com/joeshaw/envdecode"
- "github.com/pkg/browser"
- "github.com/vespa-engine/vespa/client/go/util"
"io/ioutil"
"net/http"
"os"
@@ -19,8 +16,11 @@ import (
"sync"
"time"
+ "github.com/joeshaw/envdecode"
"github.com/lestrrat-go/jwx/jwt"
+ "github.com/pkg/browser"
"github.com/vespa-engine/vespa/client/go/auth"
+ "github.com/vespa-engine/vespa/client/go/util"
)
const accessTokenExpThreshold = 5 * time.Minute
@@ -28,55 +28,24 @@ const accessTokenExpThreshold = 5 * time.Minute
var errUnauthenticated = errors.New("not logged in. Try 'vespa login'")
type config struct {
- InstallID string `json:"install_id,omitempty"`
- DefaultTenant string `json:"default_tenant"`
- Tenants map[string]Tenant `json:"tenants"`
+ Systems map[string]System `json:"systems"`
}
-// Tenant is an auth0 Tenant.
-type Tenant struct {
- Name string `json:"name"`
- Domain string `json:"domain"`
- AccessToken string `json:"access_token,omitempty"`
- Scopes []string `json:"scopes,omitempty"`
- ExpiresAt time.Time `json:"expires_at"`
- ClientID string `json:"client_id"`
- ClientSecret string `json:"client_secret"`
+type System struct {
+ AccessToken string `json:"access_token,omitempty"`
+ Scopes []string `json:"scopes,omitempty"`
+ ExpiresAt time.Time `json:"expires_at"`
}
type Cli struct {
Authenticator *auth.Authenticator
- tenant string
+ system string
initOnce sync.Once
errOnce error
Path string
config config
}
-// IsLoggedIn encodes the domain logic for determining whether we're
-// logged in. This might check our config storage, or just in memory.
-func (c *Cli) IsLoggedIn() bool {
- // No need to check errors for initializing context.
- _ = c.init()
-
- if c.tenant == "" {
- return false
- }
-
- // Parse the access token for the tenant.
- t, err := jwt.ParseString(c.config.Tenants[c.tenant].AccessToken)
- if err != nil {
- return false
- }
-
- // Check if token is valid.
- if err = jwt.Validate(t, jwt.WithIssuer("https://vespa-cd.auth0.com/")); err != nil {
- return false
- }
-
- return true
-}
-
// default to vespa-cd.auth0.com
var (
authCfg struct {
@@ -99,11 +68,12 @@ func ContextWithCancel() context.Context {
return ctx
}
-// Setup will try to initialize the config context, as well as figure out if
-// there's a readily available tenant.
-func GetCli(configPath string) (*Cli, error) {
+// GetCli will try to initialize the config context, as well as figure out if
+// there's a readily available system.
+func GetCli(configPath string, systemName string) (*Cli, error) {
c := Cli{}
c.Path = configPath
+ c.system = systemName
if err := envdecode.StrictDecode(&authCfg); err != nil {
return nil, fmt.Errorf("could not decode env: %w", err)
}
@@ -116,29 +86,49 @@ func GetCli(configPath string) (*Cli, error) {
return &c, nil
}
-// prepareTenant loads the Tenant, refreshing its token if necessary.
-// The Tenant access token needs a refresh if:
-// 1. the Tenant scopes are different from the currently required scopes.
-// 2. the access token is expired.
-func (c *Cli) PrepareTenant(ctx context.Context) (Tenant, error) {
- if err := c.init(); err != nil {
- return Tenant{}, err
+// IsLoggedIn encodes the domain logic for determining whether we're
+// logged in. This might check our config storage, or just in memory.
+func (c *Cli) IsLoggedIn() bool {
+ // No need to check errors for initializing context.
+ _ = c.init()
+
+ if c.system == "" {
+ return false
}
- t, err := c.getTenant()
+
+ // Parse the access token for the system.
+ token, err := jwt.ParseString(c.config.Systems[c.system].AccessToken)
if err != nil {
- return Tenant{}, err
+ return false
}
- if t.ClientID != "" && t.ClientSecret != "" {
- return t, nil
+ // Check if token is valid.
+ if err = jwt.Validate(token, jwt.WithIssuer("https://vespa-cd.auth0.com/")); err != nil {
+ return false
}
- if t.AccessToken == "" || scopesChanged(t) {
- t, err = RunLogin(ctx, c, true)
+ return true
+}
+
+// PrepareSystem loads the System, refreshing its token if necessary.
+// The System access token needs a refresh if:
+// 1. the System scopes are different from the currently required scopes - (auth0 changes).
+// 2. the access token is expired.
+func (c *Cli) PrepareSystem(ctx context.Context) (System, error) {
+ if err := c.init(); err != nil {
+ return System{}, err
+ }
+ s, err := c.getSystem()
+ if err != nil {
+ return System{}, err
+ }
+
+ if s.AccessToken == "" || scopesChanged(s) {
+ s, err = RunLogin(ctx, c, true)
if err != nil {
- return Tenant{}, err
+ return System{}, err
}
- } else if isExpired(t.ExpiresAt, accessTokenExpThreshold) {
+ } else if isExpired(s.ExpiresAt, accessTokenExpThreshold) {
// check if the stored access token is expired:
// use the refresh token to get a new access token:
tr := &auth.TokenRetriever{
@@ -147,29 +137,29 @@ func (c *Cli) PrepareTenant(ctx context.Context) (Tenant, error) {
Client: http.DefaultClient,
}
- res, err := tr.Refresh(ctx, t.Domain)
+ res, err := tr.Refresh(ctx, c.system)
if err != nil {
// ask and guide the user through the login process:
fmt.Println(fmt.Errorf("failed to renew access token, %s", err))
- t, err = RunLogin(ctx, c, true)
+ s, err = RunLogin(ctx, c, true)
if err != nil {
- return Tenant{}, err
+ return System{}, err
}
} else {
- // persist the updated tenant with renewed access token
- t.AccessToken = res.AccessToken
- t.ExpiresAt = time.Now().Add(
+ // persist the updated system with renewed access token
+ s.AccessToken = res.AccessToken
+ s.ExpiresAt = time.Now().Add(
time.Duration(res.ExpiresIn) * time.Second,
)
- err = c.AddTenant(t)
+ err = c.AddSystem(s)
if err != nil {
- return Tenant{}, err
+ return System{}, err
}
}
}
- return t, nil
+ return s, nil
}
// isExpired is true if now() + a threshold is after the given date
@@ -177,11 +167,11 @@ func isExpired(t time.Time, threshold time.Duration) bool {
return time.Now().Add(threshold).After(t)
}
-// scopesChanged compare the Tenant scopes
+// scopesChanged compare the System scopes
// with the currently required scopes.
-func scopesChanged(t Tenant) bool {
+func scopesChanged(s System) bool {
want := auth.RequiredScopes()
- got := t.Scopes
+ got := s.Scopes
sort.Strings(want)
sort.Strings(got)
@@ -194,7 +184,7 @@ func scopesChanged(t Tenant) bool {
return true
}
- for i := range t.Scopes {
+ for i := range s.Scopes {
if want[i] != got[i] {
return true
}
@@ -203,34 +193,30 @@ func scopesChanged(t Tenant) bool {
return false
}
-func (c *Cli) getTenant() (Tenant, error) {
+func (c *Cli) getSystem() (System, error) {
if err := c.init(); err != nil {
- return Tenant{}, err
+ return System{}, err
}
- t, ok := c.config.Tenants[c.tenant]
+ s, ok := c.config.Systems[c.system]
if !ok {
- return Tenant{}, fmt.Errorf("unable to find tenant: %s; run 'vespa login' to configure a new tenant", c.tenant)
+ return System{}, fmt.Errorf("unable to find system: %s; run 'vespa login' to configure a new system", c.system)
}
- return t, nil
+ return s, nil
}
-// AddTenant assigns an existing, or new Tenant. This is expected to be called
+// AddSystem assigns an existing, or new System. This is expected to be called
// after a login has completed.
-func (c *Cli) AddTenant(ten Tenant) error {
+func (c *Cli) AddSystem(s System) error {
_ = c.init()
- if c.config.DefaultTenant == "" {
- c.config.DefaultTenant = ten.Domain
- }
-
// If we're dealing with an empty file, we'll need to initialize this map.
- if c.config.Tenants == nil {
- c.config.Tenants = map[string]Tenant{}
+ if c.config.Systems == nil {
+ c.config.Systems = map[string]System{}
}
- c.config.Tenants[ten.Domain] = ten
+ c.config.Systems[c.system] = s
if err := c.persistConfig(); err != nil {
return fmt.Errorf("unexpected error persisting config: %w", err)
@@ -282,14 +268,6 @@ func (c *Cli) initContext() (err error) {
return err
}
- if c.tenant == "" && c.config.DefaultTenant == "" {
- return errUnauthenticated
- }
-
- if c.tenant == "" {
- c.tenant = c.config.DefaultTenant
- }
-
return nil
}
@@ -297,14 +275,14 @@ func (c *Cli) initContext() (err error) {
// by showing the login instructions, opening the browser.
// Use `expired` to run the login from other commands setup:
// this will only affect the messages.
-func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) {
+func RunLogin(ctx context.Context, c *Cli, expired bool) (System, error) {
if expired {
fmt.Println("Please sign in to re-authorize the CLI.")
}
- state, err := cli.Authenticator.Start(ctx)
+ state, err := c.Authenticator.Start(ctx)
if err != nil {
- return Tenant{}, fmt.Errorf("could not start the authentication process: %w", err)
+ return System{}, fmt.Errorf("could not start the authentication process: %w", err)
}
fmt.Printf("Your Device Confirmation code is: %s\n\n", state.UserCode)
@@ -319,12 +297,12 @@ func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) {
var res auth.Result
err = util.Spinner("Waiting for login to complete in browser", func() error {
- res, err = cli.Authenticator.Wait(ctx, state)
+ res, err = c.Authenticator.Wait(ctx, state)
return err
})
if err != nil {
- return Tenant{}, fmt.Errorf("login error: %w", err)
+ return System{}, fmt.Errorf("login error: %w", err)
}
fmt.Print("\n")
@@ -333,23 +311,21 @@ func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) {
// store the refresh token
secretsStore := &auth.Keyring{}
- err = secretsStore.Set(auth.SecretsNamespace, res.Domain, res.RefreshToken)
+ err = secretsStore.Set(auth.SecretsNamespace, c.system, res.RefreshToken)
if err != nil {
// log the error but move on
fmt.Println("Could not store the refresh token locally, please expect to login again once your access token expired.")
}
- t := Tenant{
- Name: res.Tenant,
- Domain: res.Domain,
+ s := System{
AccessToken: res.AccessToken,
ExpiresAt: time.Now().Add(time.Duration(res.ExpiresIn) * time.Second),
Scopes: auth.RequiredScopes(),
}
- err = cli.AddTenant(t)
+ err = c.AddSystem(s)
if err != nil {
- return Tenant{}, fmt.Errorf("could not add tenant to config: %w", err)
+ return System{}, fmt.Errorf("could not add system to config: %w", err)
}
- return t, nil
+ return s, nil
}
diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go
index 54d8798b71d..084148f9bdc 100644
--- a/client/go/cmd/helpers.go
+++ b/client/go/cmd/helpers.go
@@ -144,6 +144,13 @@ func getService(service string, sessionOrRunID int64) *vespa.Service {
func getSystem() string { return os.Getenv("VESPA_CLI_CLOUD_SYSTEM") }
+func getSystemName() string {
+ if getSystem() == "publiccd" {
+ return "publiccd"
+ }
+ return "public"
+}
+
func getConsoleURL() string {
if getSystem() == "publiccd" {
return "https://console-cd.vespa.oath.cloud"
@@ -153,17 +160,10 @@ func getConsoleURL() string {
}
func getApiURL() string {
- if vespa.Auth0AccessTokenEnabled() {
- if getSystem() == "publiccd" {
- return "https://api.vespa-external-cd.aws.oath.cloud:443"
- }
- return "https://api.vespa-external.aws.oath.cloud:443"
- } else {
- if getSystem() == "publiccd" {
- return "https://api.vespa-external-cd.aws.oath.cloud:4443"
- }
- return "https://api.vespa-external.aws.oath.cloud:4443"
+ if getSystem() == "publiccd" {
+ return "https://api.vespa-external-cd.aws.oath.cloud:4443"
}
+ return "https://api.vespa-external.aws.oath.cloud:4443"
}
func getTarget() vespa.Target {
@@ -212,7 +212,8 @@ func getTarget() vespa.Target {
Writer: stdout,
Level: vespa.LogLevel(logLevelArg),
},
- cfg.AuthConfigPath())
+ cfg.AuthConfigPath(),
+ getSystemName())
}
fatalErrHint(fmt.Errorf("Invalid target: %s", targetType), "Valid targets are 'local', 'cloud' or an URL")
return nil
diff --git a/client/go/cmd/login.go b/client/go/cmd/login.go
index 767d462b0be..415d44b75db 100644
--- a/client/go/cmd/login.go
+++ b/client/go/cmd/login.go
@@ -24,7 +24,7 @@ var loginCmd = &cobra.Command{
if err != nil {
return err
}
- c, err := cli.GetCli(cfg.AuthConfigPath())
+ c, err := cli.GetCli(cfg.AuthConfigPath(), getSystemName())
if err != nil {
return err
}
diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go
index f497bf5b3cd..367685df34d 100644
--- a/client/go/vespa/target.go
+++ b/client/go/vespa/target.go
@@ -1,4 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
package vespa
import (
@@ -6,7 +7,6 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
- "github.com/vespa-engine/vespa/client/go/cli"
"io"
"io/ioutil"
"math"
@@ -18,6 +18,7 @@ import (
"strings"
"time"
+ "github.com/vespa-engine/vespa/client/go/cli"
"github.com/vespa-engine/vespa/client/go/util"
)
@@ -211,6 +212,7 @@ type cloudTarget struct {
queryURL string
documentURL string
authConfigPath string
+ systemName string
}
func (t *cloudTarget) Type() string { return t.targetType }
@@ -253,12 +255,12 @@ func (t *cloudTarget) PrepareApiRequest(req *http.Request, sigKeyId string) erro
}
func (t *cloudTarget) addAuth0AccessToken(request *http.Request) error {
- c, err := cli.GetCli(t.authConfigPath)
- tenant, err := c.PrepareTenant(cli.ContextWithCancel())
+ c, err := cli.GetCli(t.authConfigPath, t.systemName)
+ system, err := c.PrepareSystem(cli.ContextWithCancel())
if err != nil {
return err
}
- request.Header.Set("Authorization", "Bearer "+tenant.AccessToken)
+ request.Header.Set("Authorization", "Bearer "+system.AccessToken)
return nil
}
@@ -446,8 +448,7 @@ func CustomTarget(baseURL string) Target {
}
// CloudTarget creates a Target for the Vespa Cloud platform.
-func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions,
- authConfigPath string) Target {
+func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions, authConfigPath string, systemName string) Target {
return &cloudTarget{
apiURL: apiURL,
targetType: cloudTargetType,
@@ -456,6 +457,7 @@ func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions
tlsOptions: tlsOptions,
logOptions: logOptions,
authConfigPath: authConfigPath,
+ systemName: systemName,
}
}
diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go
index d4d23901513..62bde3044bf 100644
--- a/client/go/vespa/target_test.go
+++ b/client/go/vespa/target_test.go
@@ -149,15 +149,10 @@ func createCloudTarget(t *testing.T, url string, logWriter io.Writer) Target {
}
assert.Nil(t, err)
- target := CloudTarget(
- "https://example.com",
- Deployment{
- Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"},
- Zone: ZoneID{Environment: "dev", Region: "us-north-1"},
- },
- apiKey,
- TLSOptions{KeyPair: x509KeyPair},
- LogOptions{Writer: logWriter}, "")
+ target := CloudTarget("https://example.com", Deployment{
+ Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"},
+ Zone: ZoneID{Environment: "dev", Region: "us-north-1"},
+ }, apiKey, TLSOptions{KeyPair: x509KeyPair}, LogOptions{Writer: logWriter}, "", "")
if ct, ok := target.(*cloudTarget); ok {
ct.apiURL = url
} else {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java
index 1c45e4ba5dd..0154e5d3b13 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java
@@ -29,12 +29,12 @@ public class ApplicationClusterEndpoint {
private final String clusterId;
private ApplicationClusterEndpoint(DnsName dnsName, Scope scope, RoutingMethod routingMethod, int weight, List<String> hostNames, String clusterId) {
- this.dnsName = Objects.requireNonNull(dnsName);
- this.scope = Objects.requireNonNull(scope);
- this.routingMethod = Objects.requireNonNull(routingMethod);
+ this.dnsName = dnsName;
+ this.scope = scope;
+ this.routingMethod = routingMethod;
this.weight = weight;
- this.hostNames = List.copyOf(Objects.requireNonNull(hostNames));
- this.clusterId = Objects.requireNonNull(clusterId);
+ this.hostNames = List.copyOf(hostNames);
+ this.clusterId = clusterId;
}
public DnsName dnsName() {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index c5781c2805d..2b2344c5fdb 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -100,11 +100,13 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"hmusum"}) default double resourceLimitMemory() { return 0.8; }
@ModelFeatureFlag(owners = {"geirst", "vekterli"}) default double minNodeRatioPerGroup() { return 0.0; }
@ModelFeatureFlag(owners = {"arnej"}) default boolean newLocationBrokerLogic() { return true; }
- @ModelFeatureFlag(owners = {"bjorncs"}) default int maxConnectionLifeInHosted() { return 45; }
+ @ModelFeatureFlag(owners = {"bjorncs"}, removeAfter = "7.504") default int maxConnectionLifeInHosted() { return 45; }
@ModelFeatureFlag(owners = {"geirst", "vekterli"}) default int distributorMergeBusyWait() { return 10; }
@ModelFeatureFlag(owners = {"vekterli", "geirst"}) default boolean distributorEnhancedMaintenanceScheduling() { return false; }
@ModelFeatureFlag(owners = {"arnej"}) default boolean forwardIssuesAsErrors() { return true; }
@ModelFeatureFlag(owners = {"geirst", "vekterli"}) default boolean asyncApplyBucketDiff() { return false; }
+ @ModelFeatureFlag(owners = {"arnej"}) default boolean ignoreThreadStackSizes() { return false; }
+ @ModelFeatureFlag(owners = {"vekterli", "geirst"}) default boolean unorderedMergeChaining() { return false; }
}
/** Warning: As elsewhere in this package, do not make backwards incompatible changes that will break old config models! */
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index 49c968a1d91..9f2f3620c73 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -72,6 +72,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
private double diskBloatFactor = 0.2;
private boolean distributorEnhancedMaintenanceScheduling = false;
private boolean asyncApplyBucketDiff = false;
+ private boolean unorderedMergeChaining = false;
private List<String> zoneDnsSuffixes = List.of();
@Override public ModelContext.FeatureFlags featureFlags() { return this; }
@@ -125,6 +126,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
@Override public boolean distributorEnhancedMaintenanceScheduling() { return distributorEnhancedMaintenanceScheduling; }
@Override public int maxUnCommittedMemory() { return maxUnCommittedMemory; }
@Override public boolean asyncApplyBucketDiff() { return asyncApplyBucketDiff; }
+ @Override public boolean unorderedMergeChaining() { return unorderedMergeChaining; }
@Override public List<String> zoneDnsSuffixes() { return zoneDnsSuffixes; }
public TestProperties maxUnCommittedMemory(int maxUnCommittedMemory) {
@@ -322,6 +324,11 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
return this;
}
+ public TestProperties setUnorderedMergeChaining(boolean unordered) {
+ unorderedMergeChaining = unordered;
+ return this;
+ }
+
public TestProperties setZoneDnsSuffixes(List<String> zoneDnsSuffixes) {
this.zoneDnsSuffixes = List.copyOf(zoneDnsSuffixes);
return this;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ConfigSentinel.java b/config-model/src/main/java/com/yahoo/vespa/model/ConfigSentinel.java
index c86cda0bbd1..217f6cff778 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/ConfigSentinel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/ConfigSentinel.java
@@ -19,6 +19,7 @@ public class ConfigSentinel extends AbstractService implements SentinelConfig.Pr
private final ApplicationId applicationId;
private final Zone zone;
+ private final boolean ignoreRequestedStackSizes;
/**
* Constructs a new ConfigSentinel for the given host.
@@ -31,6 +32,7 @@ public class ConfigSentinel extends AbstractService implements SentinelConfig.Pr
super(host, "sentinel");
this.applicationId = applicationId;
this.zone = zone;
+ this.ignoreRequestedStackSizes = featureFlags.ignoreThreadStackSizes();
portsMeta.on(0).tag("rpc").tag("admin");
portsMeta.on(1).tag("telnet").tag("interactive").tag("http").tag("state");
setProp("clustertype", "hosts");
@@ -73,6 +75,7 @@ public class ConfigSentinel extends AbstractService implements SentinelConfig.Pr
@Override
public void getConfig(SentinelConfig.Builder builder) {
builder.application(getApplicationConfig());
+ builder.ignoreRequestedStackSizes(ignoreRequestedStackSizes);
for (Service s : getHostResource().getServices()) {
if (s.getStartupCommand() != null) {
builder.service(getServiceConfig(s));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
index 3eba4ad84a6..b7bacb34b05 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
@@ -25,17 +25,16 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
private final boolean enforceClientAuth;
private final boolean enforceHandshakeClientAuth;
private final Collection<String> tlsCiphersOverride;
- private final Duration maxConnectionLife;
/**
* Create connector factory that uses a certificate provided by the config-model / configserver and default hosted Vespa truststore.
*/
public static HostedSslConnectorFactory withProvidedCertificate(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth,
- Collection<String> tlsCiphersOverride, Duration maxConnectionLife) {
+ Collection<String> tlsCiphersOverride) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth);
- return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride, maxConnectionLife);
+ return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride);
}
/**
@@ -43,28 +42,25 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
*/
public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates,
- Collection<String> tlsCiphersOverride, Duration maxConnectionLife) {
+ Collection<String> tlsCiphersOverride) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false);
- return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, maxConnectionLife);
+ return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride);
}
/**
* Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration).
*/
- public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(
- String serverName, Collection<String> tlsCiphersOverride, Duration maxConnectionLife) {
- return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride, maxConnectionLife);
+ public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName, Collection<String> tlsCiphersOverride) {
+ return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride);
}
private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth,
- boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride,
- Duration maxConnectionLife) {
+ boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride) {
super(new Builder("tls4443", 4443).sslProvider(sslProvider));
this.enforceClientAuth = enforceClientAuth;
this.enforceHandshakeClientAuth = enforceHandshakeClientAuth;
this.tlsCiphersOverride = tlsCiphersOverride;
- this.maxConnectionLife = maxConnectionLife;
}
private static ConfiguredDirectSslProvider createConfiguredDirectSslProvider(
@@ -100,6 +96,6 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
connectorBuilder
.proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder().enabled(true).mixedMode(true))
.idleTimeout(Duration.ofSeconds(30).toSeconds())
- .maxConnectionLife(maxConnectionLife.toSeconds());
+ .maxConnectionLife(Duration.ofSeconds(45).toSeconds());
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
index 527897a3266..d65fbba6a5e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
@@ -94,7 +94,6 @@ import org.w3c.dom.Node;
import java.net.URI;
import java.security.cert.X509Certificate;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -443,7 +442,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
// If the deployment contains certificate/private key reference, setup TLS port
HostedSslConnectorFactory connectorFactory;
Collection<String> tlsCiphersOverride = deployState.getProperties().tlsCiphersOverride();
- Duration maxConnectionLife = Duration.ofSeconds(deployState.featureFlags().maxConnectionLifeInHosted());
if (deployState.endpointCertificateSecrets().isPresent()) {
boolean authorizeClient = deployState.zone().system().isPublic();
if (authorizeClient && deployState.tlsClientAuthority().isEmpty()) {
@@ -458,11 +456,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
connectorFactory = authorizeClient
? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(
- serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride, maxConnectionLife)
+ serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride)
: HostedSslConnectorFactory.withProvidedCertificate(
- serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride, maxConnectionLife);
+ serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride);
} else {
- connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride, maxConnectionLife);
+ connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride);
}
cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory));
server.addConnector(connectorFactory);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
index fa91dbc2e42..518c3f73a8f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
@@ -45,6 +45,7 @@ public class DistributorCluster extends AbstractConfigProducer<Distributor> impl
private final int maxActivationInhibitedOutOfSyncGroups;
private final int mergeBusyWait;
private final boolean enhancedMaintenanceScheduling;
+ private final boolean unorderedMergeChaining;
public static class Builder extends VespaDomBuilder.DomConfigProducerBuilder<DistributorCluster> {
@@ -109,12 +110,14 @@ public class DistributorCluster extends AbstractConfigProducer<Distributor> impl
int maxInhibitedGroups = deployState.getProperties().featureFlags().maxActivationInhibitedOutOfSyncGroups();
int mergeBusyWait = deployState.getProperties().featureFlags().distributorMergeBusyWait();
boolean useEnhancedMaintenanceScheduling = deployState.getProperties().featureFlags().distributorEnhancedMaintenanceScheduling();
+ boolean unorderedMergeChaining = deployState.getProperties().featureFlags().unorderedMergeChaining();
return new DistributorCluster(parent,
new BucketSplitting.Builder().build(new ModelElement(producerSpec)), gc,
hasIndexedDocumentType, useThreePhaseUpdates,
maxInhibitedGroups, mergeBusyWait,
- useEnhancedMaintenanceScheduling);
+ useEnhancedMaintenanceScheduling,
+ unorderedMergeChaining);
}
}
@@ -123,7 +126,8 @@ public class DistributorCluster extends AbstractConfigProducer<Distributor> impl
boolean useThreePhaseUpdates,
int maxActivationInhibitedOutOfSyncGroups,
int mergeBusyWait,
- boolean enhancedMaintenanceScheduling)
+ boolean enhancedMaintenanceScheduling,
+ boolean unorderedMergeChaining)
{
super(parent, "distributor");
this.parent = parent;
@@ -134,6 +138,7 @@ public class DistributorCluster extends AbstractConfigProducer<Distributor> impl
this.maxActivationInhibitedOutOfSyncGroups = maxActivationInhibitedOutOfSyncGroups;
this.mergeBusyWait = mergeBusyWait;
this.enhancedMaintenanceScheduling = enhancedMaintenanceScheduling;
+ this.unorderedMergeChaining = unorderedMergeChaining;
}
@Override
@@ -149,6 +154,7 @@ public class DistributorCluster extends AbstractConfigProducer<Distributor> impl
builder.max_activation_inhibited_out_of_sync_groups(maxActivationInhibitedOutOfSyncGroups);
builder.inhibit_merge_sending_on_busy_node_duration_sec(mergeBusyWait);
builder.implicitly_clear_bucket_priority_on_schedule(enhancedMaintenanceScheduling);
+ builder.use_unordered_merge_chaining(unorderedMergeChaining);
bucketSplitting.getConfig(builder);
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
index 0fb7e82c095..729348a0e3a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
@@ -1119,6 +1119,23 @@ public class ContentClusterTest extends ContentBaseTest {
}
@Test
+ public void unordered_merge_chaining_config_controlled_by_properties() throws Exception {
+ assertFalse(resolveUnorderedMergeChainingConfig(false));
+ assertTrue(resolveUnorderedMergeChainingConfig(true));
+ }
+
+ private boolean resolveUnorderedMergeChainingConfig(boolean unorderedMergeChaining) throws Exception {
+ var props = new TestProperties();
+ if (unorderedMergeChaining) {
+ props.setUnorderedMergeChaining(true);
+ }
+ var cluster = createOneNodeCluster(props);
+ var builder = new StorDistributormanagerConfig.Builder();
+ cluster.getDistributorNodes().getConfig(builder);
+ return (new StorDistributormanagerConfig(builder)).use_unordered_merge_chaining();
+ }
+
+ @Test
public void testDedicatedClusterControllers() {
VespaModel noContentModel = createEnd2EndOneNode(new TestProperties().setHostedVespa(true)
.setMultitenant(true),
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConnectionPool.java b/config/src/main/java/com/yahoo/vespa/config/ConnectionPool.java
index 31f85ee4fd5..efcb30213ad 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConnectionPool.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConnectionPool.java
@@ -11,8 +11,8 @@ public interface ConnectionPool extends AutoCloseable {
Connection getCurrent();
/**
- * Switches to another (healthy, if one exists) Connection instance.
- * Returns the resulting Connection.
+ * Switches to another (healthy, if possible) Connection instance. {@link #getCurrent()} will
+ * return this instance afterwards, which is also the return value.
*
* @return a Connection
*/
diff --git a/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java b/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java
index f6708a1432c..a64121e2553 100644
--- a/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java
+++ b/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java
@@ -15,7 +15,7 @@ import java.util.logging.Logger;
/**
* A pool of JRT connections to a config source (either a config server or a config proxy).
- * The current connection is chosen randomly when calling {#link {@link #switchConnection()}}
+ * The current connection is chosen randomly when calling {@link #switchConnection(Connection)}
* (it will continue to use the same connection if there is only one source).
* The current connection is available with {@link #getCurrent()}.
*
diff --git a/configd/src/apps/sentinel/manager.cpp b/configd/src/apps/sentinel/manager.cpp
index 631f63febd5..ffa9c9281fa 100644
--- a/configd/src/apps/sentinel/manager.cpp
+++ b/configd/src/apps/sentinel/manager.cpp
@@ -79,6 +79,11 @@ Manager::doConfigure()
LOG_ASSERT(_env.configOwner().hasConfig());
const SentinelConfig& config(_env.configOwner().getConfig());
+ if (config.ignoreRequestedStackSizes) {
+ setenv("VESPA_IGNORE_REQUESTED_STACK_SIZES", "true", 1);
+ } else {
+ unsetenv("VESPA_IGNORE_REQUESTED_STACK_SIZES");
+ }
_env.rpcPort(config.port.rpc);
_env.statePort(config.port.telnet);
diff --git a/configdefinitions/src/vespa/sentinel.def b/configdefinitions/src/vespa/sentinel.def
index cccf931938a..7bd4a000055 100644
--- a/configdefinitions/src/vespa/sentinel.def
+++ b/configdefinitions/src/vespa/sentinel.def
@@ -28,6 +28,9 @@ connectivity.minOkPercent int default=50
## Absolute number of nodes with confirmed network connectivity problems, maximum
connectivity.maxBadCount int default=1
+## Ask low-level thread creation to honor requested stack size
+ignoreRequestedStackSizes bool default=false restart
+
## The command to run. This will be run by sh -c, and the following
## environment variables are defined: $ROOT, $VESPA_SERVICE_NAME,
## $VESPA_CONFIG_ID
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 0b55c4bb53e..3630215913e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -190,7 +190,6 @@ public class ModelContextImpl implements ModelContext {
private final int metricsproxyNumThreads;
private final boolean containerDumpHeapOnShutdownTimeout;
private final double containerShutdownTimeout;
- private final int maxConnectionLifeInHosted;
private final int distributorMergeBusyWait;
private final int docstoreCompressionLevel;
private final double diskBloatFactor;
@@ -198,6 +197,8 @@ public class ModelContextImpl implements ModelContext {
private final int maxUnCommittedMemory;
private final boolean forwardIssuesAsErrors;
private final boolean asyncApplyBucketDiff;
+ private final boolean ignoreThreadStackSizes;
+ private final boolean unorderedMergeChaining;
public FeatureFlags(FlagSource source, ApplicationId appId) {
this.defaultTermwiseLimit = flagValue(source, appId, Flags.DEFAULT_TERM_WISE_LIMIT);
@@ -226,7 +227,6 @@ public class ModelContextImpl implements ModelContext {
this.metricsproxyNumThreads = flagValue(source, appId, Flags.METRICSPROXY_NUM_THREADS);
this.containerDumpHeapOnShutdownTimeout = flagValue(source, appId, Flags.CONTAINER_DUMP_HEAP_ON_SHUTDOWN_TIMEOUT);
this.containerShutdownTimeout = flagValue(source, appId,Flags.CONTAINER_SHUTDOWN_TIMEOUT);
- this.maxConnectionLifeInHosted = flagValue(source, appId, Flags.MAX_CONNECTION_LIFE_IN_HOSTED);
this.distributorMergeBusyWait = flagValue(source, appId, Flags.DISTRIBUTOR_MERGE_BUSY_WAIT);
this.docstoreCompressionLevel = flagValue(source, appId, Flags.DOCSTORE_COMPRESSION_LEVEL);
this.diskBloatFactor = flagValue(source, appId, Flags.DISK_BLOAT_FACTOR);
@@ -234,6 +234,8 @@ public class ModelContextImpl implements ModelContext {
this.maxUnCommittedMemory = flagValue(source, appId, Flags.MAX_UNCOMMITTED_MEMORY);;
this.forwardIssuesAsErrors = flagValue(source, appId, PermanentFlags.FORWARD_ISSUES_AS_ERRORS);
this.asyncApplyBucketDiff = flagValue(source, appId, Flags.ASYNC_APPLY_BUCKET_DIFF);
+ this.ignoreThreadStackSizes = flagValue(source, appId, Flags.IGNORE_THREAD_STACK_SIZES);
+ this.unorderedMergeChaining = flagValue(source, appId, Flags.UNORDERED_MERGE_CHAINING);
}
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@@ -264,7 +266,6 @@ public class ModelContextImpl implements ModelContext {
@Override public int metricsproxyNumThreads() { return metricsproxyNumThreads; }
@Override public double containerShutdownTimeout() { return containerShutdownTimeout; }
@Override public boolean containerDumpHeapOnShutdownTimeout() { return containerDumpHeapOnShutdownTimeout; }
- @Override public int maxConnectionLifeInHosted() { return maxConnectionLifeInHosted; }
@Override public int distributorMergeBusyWait() { return distributorMergeBusyWait; }
@Override public double diskBloatFactor() { return diskBloatFactor; }
@Override public int docstoreCompressionLevel() { return docstoreCompressionLevel; }
@@ -272,6 +273,8 @@ public class ModelContextImpl implements ModelContext {
@Override public int maxUnCommittedMemory() { return maxUnCommittedMemory; }
@Override public boolean forwardIssuesAsErrors() { return forwardIssuesAsErrors; }
@Override public boolean asyncApplyBucketDiff() { return asyncApplyBucketDiff; }
+ @Override public boolean ignoreThreadStackSizes() { return ignoreThreadStackSizes; }
+ @Override public boolean unorderedMergeChaining() { return unorderedMergeChaining; }
private static <V> V flagValue(FlagSource source, ApplicationId appId, UnboundFlag<? extends V, ?, ?> flag) {
return flag.bindTo(source)
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index 0aeea5ce2d5..95be59e4d26 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -53,8 +53,10 @@ import org.apache.zookeeper.KeeperException;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
@@ -80,6 +82,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import static com.yahoo.vespa.curator.Curator.CompletionWaiter;
+import static java.nio.file.Files.readAttributes;
/**
*
@@ -593,9 +596,27 @@ public class SessionRepository {
return candidate.getCreateTime().plus(sessionLifetime).isBefore(clock.instant());
}
- // Sessions with state other than UNKNOWN or ACTIVATE
+ // Sessions with state other than UNKNOWN or ACTIVATE or old sessions in UNKNOWN state
private boolean canBeDeleted(LocalSession candidate) {
- return ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(candidate.getStatus());
+ return ! List.of(Session.Status.UNKNOWN, Session.Status.ACTIVATE).contains(candidate.getStatus())
+ || oldSessionDirWithNonExistingSession(candidate);
+ }
+
+ private boolean oldSessionDirWithNonExistingSession(LocalSession session) {
+ File sessionDir = tenantFileSystemDirs.getUserApplicationDir(session.getSessionId());
+ return sessionDir.exists()
+ && session.getStatus() == Session.Status.UNKNOWN
+ && created(sessionDir).plus(Duration.ofDays(30)).isBefore(clock.instant());
+ }
+
+ private Instant created(File file) {
+ BasicFileAttributes fileAttributes;
+ try {
+ fileAttributes = readAttributes(file.toPath(), BasicFileAttributes.class);
+ return fileAttributes.creationTime().toInstant();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
private void ensureSessionPathDoesNotExist(long sessionId) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index ce926016bd4..63c569fb17a 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -58,7 +58,10 @@ import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Files;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -462,10 +465,10 @@ public class ApplicationRepositoryTest {
assertEquals(1, sessionRepository.getLocalSessions().size());
// Create a local session without any data in zookeeper (corner case seen in production occasionally)
- // and check that expiring local sessions still work
+ // and check that expiring local sessions still works
int sessionId = 6;
TenantName tenantName = tester.tenant().getName();
- Files.createDirectory(new TenantFileSystemDirs(serverdb, tenantName).getUserApplicationDir(sessionId).toPath());
+ java.nio.file.Path dir = Files.createDirectory(new TenantFileSystemDirs(serverdb, tenantName).getUserApplicationDir(sessionId).toPath());
LocalSession localSession2 = new LocalSession(tenant1,
sessionId,
FilesApplicationPackage.fromFile(testApp),
@@ -490,6 +493,12 @@ public class ApplicationRepositoryTest {
// Check that trying to expire when there are no active sessions works
tester.applicationRepository().deleteExpiredLocalSessions();
+ assertEquals(2, sessionRepository.getLocalSessions().size());
+
+ // Set older created timestamp for session dir for local session without any data in zookeeper, should be deleted
+ setCreatedTime(dir, Instant.now().minus(Duration.ofDays(31)));
+ tester.applicationRepository().deleteExpiredLocalSessions();
+ assertEquals(1, sessionRepository.getLocalSessions().size());
}
@Test
@@ -740,6 +749,16 @@ public class ApplicationRepositoryTest {
return applicationRepository.getMetadataFromLocalSession(tenant, sessionId);
}
+ private void setCreatedTime(java.nio.file.Path file, Instant createdTime) {
+ try {
+ BasicFileAttributeView attributes = Files.getFileAttributeView(file, BasicFileAttributeView.class);
+ FileTime time = FileTime.fromMillis(createdTime.toEpochMilli());
+ attributes.setTimes(time, time, time);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
/** Stores all added or set values for each metric and context. */
static class MockMetric implements Metric {
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
index 44daec42b88..4b66715fcf7 100644
--- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java
@@ -76,6 +76,7 @@ class ServletOutputStreamWriter {
void writeBuffer(ByteBuffer buf, CompletionHandler handler) {
boolean thisThreadShouldWrite = false;
+ Throwable registrationFailure = null;
synchronized (monitor) {
if (state == State.FINISHED_OR_ERROR) {
@@ -85,8 +86,12 @@ class ServletOutputStreamWriter {
responseContentQueue.addLast(new ResponseContentPart(buf, handler));
switch (state) {
case NOT_STARTED:
- state = State.WAITING_FOR_WRITE_POSSIBLE_CALLBACK;
- outputStream.setWriteListener(writeListener);
+ try {
+ outputStream.setWriteListener(writeListener);
+ state = State.WAITING_FOR_WRITE_POSSIBLE_CALLBACK;
+ } catch (Throwable t) {
+ registrationFailure = t;
+ }
break;
case WAITING_FOR_WRITE_POSSIBLE_CALLBACK:
case WRITING_BUFFERS:
@@ -99,6 +104,9 @@ class ServletOutputStreamWriter {
throw new IllegalStateException("Invalid state " + state);
}
}
+ if (registrationFailure != null) {
+ setFinished(registrationFailure);
+ }
if (thisThreadShouldWrite) {
writeBuffersInQueueToOutputStream();
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ProxyResponse.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ProxyResponse.java
index 1e9a3b4c4c8..28aaa29408e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ProxyResponse.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ProxyResponse.java
@@ -5,7 +5,6 @@ import com.yahoo.container.jdisc.HttpResponse;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
import java.util.Optional;
/**
@@ -13,10 +12,10 @@ import java.util.Optional;
*/
public class ProxyResponse extends HttpResponse {
- private final String content;
+ private final byte[] content;
private final String contentType;
- public ProxyResponse(String content, String contentType, int status) {
+ public ProxyResponse(byte[] content, String contentType, int status) {
super(status);
this.content = content;
this.contentType = contentType;
@@ -24,7 +23,7 @@ public class ProxyResponse extends HttpResponse {
@Override
public void render(OutputStream outputStream) throws IOException {
- outputStream.write(content.getBytes(StandardCharsets.UTF_8));
+ outputStream.write(content);
}
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index 2d7f9b27334..f3368d0918b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -7,7 +7,6 @@ import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
@@ -44,14 +43,14 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.RestartFilter;
import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.application.ActivateResult;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackageValidator;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.DeploymentQuotaCalculator;
import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackageValidator;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
import com.yahoo.vespa.hosted.controller.certificate.EndpointCertificates;
import com.yahoo.vespa.hosted.controller.concurrent.Once;
@@ -376,9 +375,9 @@ public class ApplicationController {
&& run.testerCertificate().isPresent())
applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get());
- endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec().instance(instance.name()));
+ endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec());
- containerEndpoints = controller.routing().containerEndpointsOf(application.get(), job.application().instance(), zone);
+ containerEndpoints = controller.routing().containerEndpointsOf(application, job.application().instance(), zone);
} // Release application lock while doing the deployment, which is a lengthy task.
@@ -434,10 +433,6 @@ public class ApplicationController {
application = withoutDeletedDeployments(application, name);
}
- for (InstanceName instance : declaredInstances)
- if (applicationPackage.deploymentSpec().requireInstance(instance).concerns(Environment.prod))
- application = controller.routing().assignRotations(application, instance);
-
// Validate new deployment spec thoroughly before storing it.
controller.jobController().deploymentStatus(application.get());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 16f12b3ac07..b6b02a7cab4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -173,27 +173,41 @@ public class RoutingController {
}
/** Returns certificate DNS names (CN and SAN values) for given deployment */
- public List<String> certificateDnsNames(DeploymentId deployment) {
+ public List<String> certificateDnsNames(DeploymentId deployment, DeploymentSpec deploymentSpec) {
List<String> endpointDnsNames = new ArrayList<>();
// We add first an endpoint name based on a hash of the application ID,
// as the certificate provider requires the first CN to be < 64 characters long.
endpointDnsNames.add(commonNameHashOf(deployment.applicationId(), controller.system()));
- // Add wildcard names for global endpoints when deploying to production
List<Endpoint.EndpointBuilder> builders = new ArrayList<>();
if (deployment.zoneId().environment().isProduction()) {
+ // Add default and wildcard names for global endpoints
builders.add(Endpoint.of(deployment.applicationId()).target(EndpointId.defaultId()));
builders.add(Endpoint.of(deployment.applicationId()).wildcard());
+
+ // Add default and wildcard names for each region targeted by application endpoints
+ List<DeploymentId> deploymentTargets = deploymentSpec.endpoints().stream()
+ .map(com.yahoo.config.application.api.Endpoint::targets)
+ .flatMap(Collection::stream)
+ .map(com.yahoo.config.application.api.Endpoint.Target::region)
+ .distinct()
+ .map(region -> new DeploymentId(deployment.applicationId(), ZoneId.from(Environment.prod, region)))
+ .collect(Collectors.toUnmodifiableList());
+ for (var targetDeployment : deploymentTargets) {
+ builders.add(Endpoint.of(targetDeployment.applicationId()).targetApplication(EndpointId.defaultId(), targetDeployment));
+ builders.add(Endpoint.of(targetDeployment.applicationId()).wildcardApplication(targetDeployment));
+ }
}
- // Add wildcard names for zone endpoints
+ // Add default and wildcard names for zone endpoints
builders.add(Endpoint.of(deployment.applicationId()).target(ClusterSpec.Id.from("default"), deployment));
builders.add(Endpoint.of(deployment.applicationId()).wildcard(deployment));
- // Build all endpoints
+ // Build all certificate names
for (var builder : builders) {
- Endpoint endpoint = builder.routingMethod(RoutingMethod.exclusive)
+ Endpoint endpoint = builder.certificateName()
+ .routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(controller.system());
endpointDnsNames.add(endpoint.dnsName());
@@ -227,7 +241,7 @@ public class RoutingController {
* Assigns one or more global rotations to given application, if eligible. The given application is implicitly
* stored, ensuring that the assigned rotation(s) are persisted when this returns.
*/
- public LockedApplication assignRotations(LockedApplication application, InstanceName instanceName) {
+ private LockedApplication assignRotations(LockedApplication application, InstanceName instanceName) {
try (RotationLock rotationLock = rotationRepository.lock()) {
var rotations = rotationRepository.getOrAssignRotations(application.get().deploymentSpec(),
application.get().require(instanceName),
@@ -239,14 +253,21 @@ public class RoutingController {
}
/** Returns the global and application-level endpoints for given deployment, as container endpoints */
- public Set<ContainerEndpoint> containerEndpointsOf(Application application, InstanceName instanceName, ZoneId zone) {
- Instance instance = application.require(instanceName);
- boolean registerLegacyNames = requiresLegacyNames(application.deploymentSpec(), instanceName);
+ public Set<ContainerEndpoint> containerEndpointsOf(LockedApplication application, InstanceName instanceName, ZoneId zone) {
+ // Assign rotations to application
+ for (var deploymentInstanceSpec : application.get().deploymentSpec().instances()) {
+ if (deploymentInstanceSpec.concerns(Environment.prod)) {
+ application = controller.routing().assignRotations(application, deploymentInstanceSpec.name());
+ }
+ }
+
+ // Add endpoints backed by a rotation, and register them in DNS if necessary
+ boolean registerLegacyNames = requiresLegacyNames(application.get().deploymentSpec(), instanceName);
+ Instance instance = application.get().require(instanceName);
Set<ContainerEndpoint> containerEndpoints = new HashSet<>();
DeploymentId deployment = new DeploymentId(instance.id(), zone);
- EndpointList endpoints = declaredEndpointsOf(application).targets(deployment);
+ EndpointList endpoints = declaredEndpointsOf(application.get()).targets(deployment);
EndpointList globalEndpoints = endpoints.scope(Endpoint.Scope.global);
- // Add endpoints backed by a rotation, and register them in DNS if necessary
for (var assignedRotation : instance.rotations()) {
var names = new ArrayList<String>();
EndpointList rotationEndpoints = globalEndpoints.named(assignedRotation.endpointId())
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 35601dd94dd..1e917dd1376 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -46,7 +46,7 @@ public class Endpoint {
private Endpoint(TenantAndApplicationId application, Optional<InstanceName> instanceName, EndpointId id,
ClusterSpec.Id cluster, URI url, List<Target> targets, Scope scope, Port port, boolean legacy,
- RoutingMethod routingMethod) {
+ RoutingMethod routingMethod, boolean certificateName) {
Objects.requireNonNull(application, "application must be non-null");
Objects.requireNonNull(instanceName, "instanceName must be non-null");
Objects.requireNonNull(cluster, "cluster must be non-null");
@@ -56,6 +56,9 @@ public class Endpoint {
Objects.requireNonNull(routingMethod, "routingMethod must be non-null");
if (scope.multiDeployment()) {
if (id == null) throw new IllegalArgumentException("Endpoint ID must be set for multi-deployment endpoints");
+ if (!certificateName && targets.isEmpty()) {
+ throw new IllegalArgumentException("At least one target must be given for " + scope + " endpoints");
+ }
} else {
if (scope == Scope.zone && id != null) throw new IllegalArgumentException("Endpoint ID cannot be set for " + scope + " endpoints");
if (targets.size() != 1) throw new IllegalArgumentException("A single target must be given for " + scope + " endpoints");
@@ -89,11 +92,14 @@ public class Endpoint {
this.legacy = legacy;
this.routingMethod = routingMethod;
this.tls = port.tls;
+ if (!certificateName && "*".equals(name())) {
+ throw new IllegalArgumentException("Wildcard found in endpoint that is not intended as a certificate name");
+ }
}
private Endpoint(EndpointId id, ClusterSpec.Id cluster, TenantAndApplicationId application,
Optional<InstanceName> instance, List<Target> targets, Scope scope, SystemName system, Port port,
- boolean legacy, RoutingMethod routingMethod) {
+ boolean legacy, RoutingMethod routingMethod, boolean certificateName) {
this(application,
instance,
id,
@@ -107,7 +113,7 @@ public class Endpoint {
Objects.requireNonNull(port, "port must be non-null"),
legacy,
routingMethod),
- targets, scope, port, legacy, routingMethod);
+ targets, scope, port, legacy, routingMethod, certificateName);
}
/**
@@ -446,7 +452,7 @@ public class Endpoint {
ClusterSpec.Id.from("admin"),
url,
List.of(new Target(new DeploymentId(systemApplication.id(), zone))),
- Scope.zone, port, false, routingMethod);
+ Scope.zone, port, false, routingMethod, false);
}
/** A target of an endpoint */
@@ -491,6 +497,7 @@ public class Endpoint {
private Port port;
private RoutingMethod routingMethod = RoutingMethod.shared;
private boolean legacy = false;
+ private boolean certificateName = false;
private EndpointBuilder(TenantAndApplicationId application, Optional<InstanceName> instance) {
this.application = Objects.requireNonNull(application);
@@ -499,33 +506,41 @@ public class Endpoint {
/** Sets the deployment target for this */
public EndpointBuilder target(ClusterSpec.Id cluster, DeploymentId deployment) {
- checkScope();
this.cluster = cluster;
- this.scope = Scope.zone;
+ this.scope = requireUnset(Scope.zone);
this.targets = List.of(new Target(deployment));
return this;
}
- /** Sets the global target with given ID and pointing to the default cluster */
- public EndpointBuilder target(EndpointId endpointId) {
- return target(endpointId, ClusterSpec.Id.from("default"), List.of());
- }
-
/** Sets the global target with given ID, deployments and cluster (as defined in deployments.xml) */
public EndpointBuilder target(EndpointId endpointId, ClusterSpec.Id cluster, List<DeploymentId> deployments) {
- checkScope();
this.endpointId = endpointId;
this.cluster = cluster;
this.targets = deployments.stream().map(Target::new).collect(Collectors.toUnmodifiableList());
- this.scope = Scope.global;
+ this.scope = requireUnset(Scope.global);
return this;
}
+ /** Sets the global target with given ID and pointing to the default cluster */
+ public EndpointBuilder target(EndpointId endpointId) {
+ return target(endpointId, ClusterSpec.Id.from("default"), List.of());
+ }
+
+ /** Sets the application target with given ID and pointing to the default cluster */
+ public EndpointBuilder targetApplication(EndpointId endpointId, DeploymentId deployment) {
+ return targetApplication(endpointId, ClusterSpec.Id.from("default"), Map.of(deployment, 1));
+ }
+
/** Sets the global wildcard target for this */
public EndpointBuilder wildcard() {
return target(EndpointId.of("*"), ClusterSpec.Id.from("*"), List.of());
}
+ /** Sets the application wildcard target for this */
+ public EndpointBuilder wildcardApplication(DeploymentId deployment) {
+ return targetApplication(EndpointId.of("*"), ClusterSpec.Id.from("*"), Map.of(deployment, 1));
+ }
+
/** Sets the zone wildcard target for this */
public EndpointBuilder wildcard(DeploymentId deployment) {
return target(ClusterSpec.Id.from("*"), deployment);
@@ -533,7 +548,6 @@ public class Endpoint {
/** Sets the application target with given ID, cluster, deployments and their weights */
public EndpointBuilder targetApplication(EndpointId endpointId, ClusterSpec.Id cluster, Map<DeploymentId, Integer> deployments) {
- checkScope();
this.endpointId = endpointId;
this.cluster = cluster;
this.targets = deployments.entrySet().stream()
@@ -545,9 +559,8 @@ public class Endpoint {
/** Sets the region target for this, deduced from given zone */
public EndpointBuilder targetRegion(ClusterSpec.Id cluster, ZoneId zone) {
- checkScope();
this.cluster = cluster;
- this.scope = Scope.weighted;
+ this.scope = requireUnset(Scope.weighted);
this.targets = List.of(new Target(new DeploymentId(application.instance(instance.get()), effectiveZone(zone))));
return this;
}
@@ -570,6 +583,12 @@ public class Endpoint {
return this;
}
+ /** Sets whether we're building a name for inclusion in a certificate */
+ public EndpointBuilder certificateName() {
+ this.certificateName = true;
+ return this;
+ }
+
/** Sets the system that owns this */
public Endpoint in(SystemName system) {
if (system.isPublic() && routingMethod != RoutingMethod.exclusive) {
@@ -578,13 +597,14 @@ public class Endpoint {
if (routingMethod.isDirect() && !port.isDefault()) {
throw new IllegalArgumentException("Routing method " + routingMethod + " can only use default port");
}
- return new Endpoint(endpointId, cluster, application, instance, targets, scope, system, port, legacy, routingMethod);
+ return new Endpoint(endpointId, cluster, application, instance, targets, scope, system, port, legacy, routingMethod, certificateName);
}
- private void checkScope() {
- if (scope != null) {
+ private Scope requireUnset(Scope scope) {
+ if (this.scope != null) {
throw new IllegalArgumentException("Cannot change endpoint scope. Already set to " + scope);
}
+ return scope;
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
index 684648ed70a..e3091b704e4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.certificate;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
+import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.text.Text;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -52,9 +53,9 @@ public class EndpointCertificates {
}
/** Returns certificate metadata for endpoints of given instance and zone */
- public Optional<EndpointCertificateMetadata> getMetadata(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
+ public Optional<EndpointCertificateMetadata> getMetadata(Instance instance, ZoneId zone, DeploymentSpec deploymentSpec) {
Instant start = clock.instant();
- Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, instanceSpec);
+ Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, deploymentSpec);
metadata.ifPresent(m -> curator.writeEndpointCertificateMetadata(instance.id(), m.withLastRequested(clock.instant().getEpochSecond())));
Duration duration = Duration.between(start, clock.instant());
if (duration.toSeconds() > 30)
@@ -62,13 +63,12 @@ public class EndpointCertificates {
return metadata;
}
- private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
- final var currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
-
+ private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, DeploymentSpec deploymentSpec) {
+ Optional<EndpointCertificateMetadata> currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
DeploymentId deployment = new DeploymentId(instance.id(), zone);
if (currentCertificateMetadata.isEmpty()) {
- var provisionedCertificateMetadata = provisionEndpointCertificate(deployment, Optional.empty(), instanceSpec);
+ var provisionedCertificateMetadata = provisionEndpointCertificate(deployment, Optional.empty(), deploymentSpec);
// We do not verify the certificate if one has never existed before - because we do not want to
// wait for it to be available before we deploy. This allows the config server to start
// provisioning nodes ASAP, and the risk is small for a new deployment.
@@ -77,10 +77,10 @@ public class EndpointCertificates {
}
// Re-provision certificate if it is missing SANs for the zone we are deploying to
- var requiredSansForZone = controller.routing().certificateDnsNames(deployment);
+ var requiredSansForZone = controller.routing().certificateDnsNames(deployment, deploymentSpec);
if (!currentCertificateMetadata.get().requestedDnsSans().containsAll(requiredSansForZone)) {
var reprovisionedCertificateMetadata =
- provisionEndpointCertificate(deployment, currentCertificateMetadata, instanceSpec)
+ provisionEndpointCertificate(deployment, currentCertificateMetadata, deploymentSpec)
.withRequestId(currentCertificateMetadata.get().requestId()); // We're required to keep the original request ID
curator.writeEndpointCertificateMetadata(instance.id(), reprovisionedCertificateMetadata);
// Verification is unlikely to succeed in this case, as certificate must be available first - controller will retry
@@ -94,12 +94,13 @@ public class EndpointCertificates {
private EndpointCertificateMetadata provisionEndpointCertificate(DeploymentId deployment,
Optional<EndpointCertificateMetadata> currentMetadata,
- Optional<DeploymentInstanceSpec> instanceSpec) {
+ DeploymentSpec deploymentSpec) {
List<ZoneId> zonesInSystem = controller.zoneRegistry().zones().controllerUpgraded().ids();
Set<ZoneId> requiredZones = new LinkedHashSet<>();
requiredZones.add(deployment.zoneId());
if (!deployment.zoneId().environment().isManuallyDeployed()) {
// If not deploying to a dev or perf zone, require all prod zones in deployment spec + test and staging
+ Optional<DeploymentInstanceSpec> instanceSpec = deploymentSpec.instance(deployment.applicationId().instance());
zonesInSystem.stream()
.filter(zone -> zone.environment().isTest() ||
(instanceSpec.isPresent() &&
@@ -107,14 +108,16 @@ public class EndpointCertificates {
.forEach(requiredZones::add);
}
Set<String> requiredNames = requiredZones.stream()
- .flatMap(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)).stream())
+ .flatMap(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone),
+ deploymentSpec)
+ .stream())
.collect(Collectors.toCollection(LinkedHashSet::new));
// Preserve any currently present names that are still valid
List<String> currentNames = currentMetadata.map(EndpointCertificateMetadata::requestedDnsSans)
.orElseGet(List::of);
zonesInSystem.stream()
- .map(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)))
+ .map(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone), deploymentSpec))
.filter(currentNames::containsAll)
.forEach(requiredNames::addAll);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
index 1c29bdd397f..2b9e3dd0733 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.deployment;
-import com.google.common.collect.ImmutableList;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import java.security.cert.X509Certificate;
@@ -267,26 +266,26 @@ public class Run {
/** Returns the list of unfinished steps whose prerequisites have all succeeded. */
private List<Step> normalSteps() {
- return ImmutableList.copyOf(steps.entrySet().stream()
- .filter(entry -> entry.getValue().status() == unfinished
- && entry.getKey().prerequisites().stream()
- .allMatch(step -> steps.get(step) == null
- || steps.get(step).status() == succeeded))
- .map(Map.Entry::getKey)
- .iterator());
+ return steps.entrySet().stream()
+ .filter(entry -> entry.getValue().status() == unfinished
+ && entry.getKey().prerequisites().stream()
+ .allMatch(step -> steps.get(step) == null
+ || steps.get(step).status() == succeeded))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toUnmodifiableList());
}
/** Returns the list of not-yet-run run-always steps whose run-always prerequisites have all run. */
private List<Step> forcedSteps() {
- return ImmutableList.copyOf(steps.entrySet().stream()
- .filter(entry -> entry.getValue().status() == unfinished
- && entry.getKey().alwaysRun()
- && entry.getKey().prerequisites().stream()
- .filter(Step::alwaysRun)
- .allMatch(step -> steps.get(step) == null
- || steps.get(step).status() != unfinished))
- .map(Map.Entry::getKey)
- .iterator());
+ return steps.entrySet().stream()
+ .filter(entry -> entry.getValue().status() == unfinished
+ && entry.getKey().alwaysRun()
+ && entry.getKey().prerequisites().stream()
+ .filter(Step::alwaysRun)
+ .allMatch(step -> steps.get(step) == null
+ || steps.get(step).status() != unfinished))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toUnmodifiableList());
}
private void requireActive() {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index ef129dd76f7..5cd5a70e4a4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -2042,19 +2042,20 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
return new MessageResponse("Deactivated " + id);
}
- /** Returns test config for indicated job, with production deployments of the default instance. */
+ /** Returns test config for indicated job, with production deployments of the default instance if the given is not in deployment spec. */
private HttpResponse testConfig(ApplicationId id, JobType type) {
- // TODO jonmv: Support non-default instances as well; requires API change in clients.
- ApplicationId defaultInstanceId = TenantAndApplicationId.from(id).defaultInstance();
+ Application application = controller.applications().requireApplication(TenantAndApplicationId.from(id));
+ ApplicationId prodInstanceId = application.deploymentSpec().instance(id.instance()).isPresent()
+ ? id : TenantAndApplicationId.from(id).defaultInstance();
HashSet<DeploymentId> deployments = controller.applications()
- .getInstance(defaultInstanceId).stream()
- .flatMap(instance -> instance.productionDeployments().keySet().stream())
- .map(zone -> new DeploymentId(defaultInstanceId, zone))
- .collect(Collectors.toCollection(HashSet::new));
- var testedZone = type.zone(controller.system());
-
- // If a production job is specified, the production deployment of the _default instance_ is the relevant one,
- // as user instances should not exist in prod. TODO jonmv: Remove this when multiple instances are supported (above).
+ .getInstance(prodInstanceId).stream()
+ .flatMap(instance -> instance.productionDeployments().keySet().stream())
+ .map(zone -> new DeploymentId(prodInstanceId, zone))
+ .collect(Collectors.toCollection(HashSet::new));
+ ZoneId testedZone = type.zone(controller.system());
+
+ // If a production job is specified, the production deployment of the orchestrated instance is the relevant one,
+ // as user instances should not exist in prod.
if ( ! type.isProduction())
deployments.add(new DeploymentId(id, testedZone));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index c1f9137ecfa..fa4a871d423 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -27,122 +27,128 @@ public class EndpointTest {
@Test
public void global_endpoints() {
+ DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
+ ClusterSpec.Id cluster = ClusterSpec.Id.from("default");
EndpointId endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
"http://a1.t1.global.vespa.yahooapis.com:4080/",
- Endpoint.of(instance1).target(endpointId).on(Port.plain(4080)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.plain(4080)).legacy().in(SystemName.main),
// Legacy endpoint with TLS
"https://a1--t1.global.vespa.yahooapis.com:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).legacy().in(SystemName.main),
// Main endpoint
"https://a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint in CD
"https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint with custom rotation name
"https://r1.a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.of("r1"), cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance in default rotation
"https://i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
"https://r2.i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(EndpointId.of("r2"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
Map<String, Endpoint> tests2 = Map.of(
// Default endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
// Default endpoint in public CD system
"https://a1.t1.g.cd.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
// Custom instance in public system
"https://i2.a2.t2.g.vespa-app.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
@Test
public void global_endpoints_with_endpoint_id() {
- var endpointId = EndpointId.defaultId();
+ DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
+ ClusterSpec.Id cluster = ClusterSpec.Id.from("default");
+ EndpointId endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
"http://a1.t1.global.vespa.yahooapis.com:4080/",
- Endpoint.of(instance1).target(endpointId).on(Port.plain(4080)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.plain(4080)).legacy().in(SystemName.main),
// Legacy endpoint with TLS
"https://a1--t1.global.vespa.yahooapis.com:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).legacy().in(SystemName.main),
// Main endpoint
"https://a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
"https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint with custom rotation name
"https://r1.a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.of("r1"), cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance in default rotation
"https://i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
"https://r2.i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(EndpointId.of("r2"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
Map<String, Endpoint> tests2 = Map.of(
// Custom endpoint and instance in public CD system)
"https://foo.i2.a2.t2.g.cd.vespa-app.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("foo")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
+ Endpoint.of(instance2).target(EndpointId.of("foo"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
// Custom endpoint and instance in public system
"https://foo.i2.a2.t2.g.vespa-app.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("foo")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance2).target(EndpointId.of("foo"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
@@ -228,6 +234,7 @@ public class EndpointTest {
"https://a1.t1.g.vespa-app.cloud/",
Endpoint.of(instance1)
.target(EndpointId.defaultId())
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -236,6 +243,7 @@ public class EndpointTest {
"https://*.a1.t1.g.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard()
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -244,6 +252,7 @@ public class EndpointTest {
"https://a1.t1.us-north-1.z.vespa-app.cloud/",
Endpoint.of(instance1)
.target(defaultCluster, prodZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -252,6 +261,7 @@ public class EndpointTest {
"https://a1.t1.us-north-2.test.z.vespa-app.cloud/",
Endpoint.of(instance1)
.target(defaultCluster, testZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -260,6 +270,7 @@ public class EndpointTest {
"https://*.a1.t1.us-north-2.test.z.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard(testZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -268,6 +279,7 @@ public class EndpointTest {
"https://*.a1.t1.us-north-1.z.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard(prodZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public)
@@ -353,7 +365,7 @@ public class EndpointTest {
var tests1 = Map.of(
// With default cluster
"a1.t1.us-north-1.prod",
- Endpoint.of(instance1).target(EndpointId.defaultId()).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main),
// With non-default cluster
"c1.a1.t1.us-north-1.prod",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
index c6a9c12027f..0a93c936b41 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
@@ -5,6 +5,8 @@ import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
@@ -19,6 +21,8 @@ import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidatorImpl;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import org.junit.Before;
@@ -31,6 +35,7 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -111,7 +116,7 @@ public class EndpointCertificatesTest {
@Test
public void provisions_new_certificate_in_dev() {
ZoneId testZone = tester.zoneRegistry().zones().routingMethod(RoutingMethod.exclusive).in(Environment.dev).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -121,7 +126,7 @@ public class EndpointCertificatesTest {
@Test
public void provisions_new_certificate_in_prod() {
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -145,7 +150,7 @@ public class EndpointCertificatesTest {
"default.default.aws-us-east-1c.staging.z.vespa-app.cloud",
"*.default.default.aws-us-east-1c.staging.z.vespa-app.cloud"
);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -164,7 +169,7 @@ public class EndpointCertificatesTest {
"", Optional.empty(), Optional.empty()));
secretStore.setSecret(testKeyName, KeyUtils.toPem(testKeyPair.getPrivate()), 7);
secretStore.setSecret(testCertName, X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 7);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(testKeyName, endpointCertificateMetadata.get().keyName());
assertEquals(testCertName, endpointCertificateMetadata.get().certName());
@@ -176,7 +181,7 @@ public class EndpointCertificatesTest {
mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "uuid", List.of(), "issuer", Optional.empty(), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
@@ -193,7 +198,7 @@ public class EndpointCertificatesTest {
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate2) + X509CertificateUtils.toPem(testCertificate2), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
@@ -203,7 +208,6 @@ public class EndpointCertificatesTest {
@Test
public void includes_zones_in_deployment_spec_when_deploying_to_staging() {
-
DeploymentSpec deploymentSpec = new DeploymentSpecXmlReader(true).read(
"<deployment version=\"1.0\">\n" +
" <instance id=\"default\">\n" +
@@ -215,7 +219,7 @@ public class EndpointCertificatesTest {
"</deployment>\n");
ZoneId testZone = tester.zoneRegistry().zones().all().in(Environment.staging).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.of(deploymentSpec.requireInstance("default")));
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, deploymentSpec);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -223,4 +227,49 @@ public class EndpointCertificatesTest {
assertEquals(Set.copyOf(expectedCombinedSans), Set.copyOf(endpointCertificateMetadata.get().requestedDnsSans()));
}
+ @Test
+ public void includes_application_endpoint_when_declared() {
+ Instance instance = new Instance(ApplicationId.from("t1", "a1", "default"));
+ ZoneId zone1 = ZoneId.from(Environment.prod, RegionName.from("aws-us-east-1c"));
+ ZoneId zone2 = ZoneId.from(Environment.prod, RegionName.from("aws-us-west-2a"));
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .instances("beta,main")
+ .region(zone1.region())
+ .region(zone2.region())
+ .applicationEndpoint("a", "qrs", zone2.region().value(),
+ Map.of(InstanceName.from("beta"), 2,
+ InstanceName.from("main"), 8))
+ .applicationEndpoint("b", "qrs", zone2.region().value(),
+ Map.of(InstanceName.from("beta"), 1,
+ InstanceName.from("main"), 1))
+ .applicationEndpoint("c", "qrs", zone1.region().value(),
+ Map.of(InstanceName.from("beta"), 4,
+ InstanceName.from("main"), 6))
+ .build();
+ ControllerTester tester = new ControllerTester(SystemName.Public);
+ EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
+ EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
+ List<String> expectedSans = List.of(
+ "vlfms2wpoa4nyrka2s5lktucypjtxkqhv.internal.vespa-app.cloud",
+ "a1.t1.g.vespa-app.cloud",
+ "*.a1.t1.g.vespa-app.cloud",
+ "a1.t1.aws-us-west-2a.r.vespa-app.cloud",
+ "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.r.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.r.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.z.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.test.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.test.z.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.staging.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.staging.z.vespa-app.cloud"
+ );
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone1, applicationPackage.deploymentSpec());
+ assertTrue(endpointCertificateMetadata.isPresent());
+ assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.t1.a1.*-key"));
+ assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.t1.a1.*-cert"));
+ assertEquals(0, endpointCertificateMetadata.get().version());
+ assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans());
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index b9c1e9f3e72..d98789591ab 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -53,6 +53,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed;
import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
import static org.junit.Assert.assertEquals;
@@ -292,13 +293,32 @@ public class DeploymentContext {
/** Fail current deployment in given job */
private DeploymentContext failDeployment(JobType type, RuntimeException exception) {
+ configServer().throwOnNextPrepare(exception);
+ runJobExpectingFailure(type, Optional.empty());
+ return this;
+ }
+
+ /** Run given job and expect it to fail with given message, if any */
+ public DeploymentContext runJobExpectingFailure(JobType type, Optional<String> messagePart) {
triggerJobs();
var job = jobId(type);
RunId id = currentRun(job).id();
- configServer().throwOnNextPrepare(exception);
runner.advance(currentRun(job));
- assertTrue(jobs.run(id).get().hasFailed());
- assertTrue(jobs.run(id).get().hasEnded());
+ Run run = jobs.run(id).get();
+ assertTrue(run.hasFailed());
+ assertTrue(run.hasEnded());
+ if (messagePart.isPresent()) {
+ Optional<Step> firstFailing = run.stepStatuses().entrySet().stream()
+ .filter(kv -> kv.getValue() == failed)
+ .map(Map.Entry::getKey)
+ .findFirst();
+ assertTrue("Found failing step", firstFailing.isPresent());
+ Optional<RunLog> details = jobs.details(id);
+ assertTrue("Found log entries for run " + id, details.isPresent());
+ assertTrue("Found log message containing '" + messagePart.get() + "'",
+ details.get().get(firstFailing.get()).stream()
+ .anyMatch(entry -> entry.message().contains(messagePart.get())));
+ }
return this;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
index 4ad70c4e9dd..61eca05cc67 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
@@ -2,9 +2,11 @@
package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
@@ -31,7 +33,9 @@ public class TestConfigSerializerTest {
JobType.systemTest,
true,
Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId())
- .target(EndpointId.of("ai"))
+ .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"),
+ List.of(new DeploymentId(ApplicationId.defaultId(),
+ ZoneId.defaultId())))
.on(Endpoint.Port.tls())
.in(SystemName.main))),
Map.of(zone, List.of("facts")));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index a3674fa27bd..f2fc624630c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -567,7 +567,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
@Override
public ProxyResponse getApplicationPackageContent(DeploymentId deployment, String path, URI requestUri) {
- return new ProxyResponse("{\"path\":\"" + path + "\"}", "application/json", 200);
+ return new ProxyResponse(("{\"path\":\"" + path + "\"}").getBytes(StandardCharsets.UTF_8), "application/json", 200);
}
public void setLogStream(Supplier<String> log) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
index b767e8a791f..e7c2eacbd02 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
@@ -5,19 +5,20 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
+import com.yahoo.vespa.hosted.controller.application.SystemApplication;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import java.net.URI;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -30,22 +31,19 @@ import static org.junit.Assert.assertTrue;
*/
public class RotationRepositoryTest {
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- private final RotationsConfig rotationsConfig = new RotationsConfig(
+ private static final RotationsConfig rotationsConfig = new RotationsConfig(
new RotationsConfig.Builder()
.rotations("foo-1", "foo-1.com")
.rotations("foo-2", "foo-2.com")
);
- private final RotationsConfig rotationsConfigWhitespaces = new RotationsConfig(
+ private static final RotationsConfig rotationsConfigWhitespaces = new RotationsConfig(
new RotationsConfig.Builder()
.rotations("foo-1", "\n \t foo-1.com \n")
.rotations("foo-2", "foo-2.com")
);
- private final ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ private static final ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.globalServiceId("foo")
.region("us-east-3")
.region("us-west-1")
@@ -57,8 +55,8 @@ public class RotationRepositoryTest {
@Test
public void assigns_and_reuses_rotation() {
- // Submitting assigns a rotation
- application.submit(applicationPackage);
+ // Deploying assigns a rotation
+ application.submit(applicationPackage).deploy();
Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com");
assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations()));
@@ -108,17 +106,16 @@ public class RotationRepositoryTest {
@Test
public void out_of_rotations() {
// Assigns 1 rotation
- application.submit(applicationPackage);
+ application.submit(applicationPackage).deploy();
// Assigns 1 more
var application2 = tester.newDeploymentContext("tenant2", "app2", "default");
- application2.submit(applicationPackage);
+ application2.submit(applicationPackage).deploy();
- // We're now out of rotations
- thrown.expect(IllegalStateException.class);
- thrown.expectMessage("out of rotations");
+ // We're now out of rotations and next deployment fails
var application3 = tester.newDeploymentContext("tenant3", "app3", "default");
- application3.submit(applicationPackage);
+ application3.submit(applicationPackage)
+ .runJobExpectingFailure(JobType.systemTest, Optional.of("out of rotations"));
}
@Test
@@ -127,9 +124,7 @@ public class RotationRepositoryTest {
.globalServiceId("foo")
.region("us-east-3")
.build();
- thrown.expect(RuntimeException.class);
- thrown.expectMessage("less than 2 prod zones are defined");
- application.submit(applicationPackage);
+ application.submit(applicationPackage).runJobExpectingFailure(JobType.systemTest, Optional.of("less than 2 prod zones are defined"));
}
@Test
@@ -149,13 +144,18 @@ public class RotationRepositoryTest {
.region("cd-us-east-1")
.region("cd-us-west-1")
.build();
- var zones = List.of(ZoneApiMock.fromId("prod.cd-us-east-1"), ZoneApiMock.fromId("prod.cd-us-west-1"));
+ var zones = List.of(
+ ZoneApiMock.fromId("test.cd-us-west-1"),
+ ZoneApiMock.fromId("staging.cd-us-west-1"),
+ ZoneApiMock.fromId("prod.cd-us-east-1"),
+ ZoneApiMock.fromId("prod.cd-us-west-1"));
tester.controllerTester().zoneRegistry()
.setZones(zones)
.setRoutingMethod(zones, RoutingMethod.shared)
.setSystemName(SystemName.cd);
+ tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.notController());
var application2 = tester.newDeploymentContext("tenant2", "app2", "default");
- application2.submit(applicationPackage);
+ application2.submit(applicationPackage).deploy();
assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations()));
assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/",
tester.controller().routing().readDeclaredEndpointsOf(application2.instanceId()).primary().get().url().toString());
@@ -169,7 +169,9 @@ public class RotationRepositoryTest {
.parallel("us-west-1", "us-east-3")
.globalServiceId("global")
.build();
- var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1").submit(applicationPackage);
+ var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1")
+ .submit(applicationPackage)
+ .deploy();
var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2");
assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations()));
assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations()));
@@ -187,7 +189,9 @@ public class RotationRepositoryTest {
.parallel("us-west-1", "us-east-3")
.endpoint("default", "foo", "us-central-1", "us-west-1")
.build();
- var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1").submit(applicationPackage);
+ var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1")
+ .submit(applicationPackage)
+ .deploy();
var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2");
assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations()));
diff --git a/default_build_settings.cmake b/default_build_settings.cmake
index 36368cbce3e..e482439dd7d 100644
--- a/default_build_settings.cmake
+++ b/default_build_settings.cmake
@@ -36,10 +36,6 @@ function(setup_vespa_default_build_settings_centos_8)
else()
set(DEFAULT_VESPA_LLVM_VERSION "11" PARENT_SCOPE)
endif()
- if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
- set(DEFAULT_CMAKE_SHARED_LINKER_FLAGS " -latomic " PARENT_SCOPE)
- set(DEFAULT_CMAKE_LINKER_FLAGS " -latomic " PARENT_SCOPE)
- endif()
endfunction()
function(setup_vespa_default_build_settings_rocky_8_4)
@@ -236,6 +232,7 @@ function(vespa_use_default_build_settings)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${DEFAULT_CMAKE_SHARED_LINKER_FLAGS}" PARENT_SCOPE)
endif()
if(NOT DEFINED DEFAULT_VESPA_CPU_ARCH_FLAGS)
+ message("-- CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
if(VESPA_OS_DISTRO STREQUAL "fedora" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(DEFAULT_VESPA_CPU_ARCH_FLAGS "-march=westmere -mtune=haswell")
@@ -243,6 +240,8 @@ function(vespa_use_default_build_settings)
else()
set(DEFAULT_VESPA_CPU_ARCH_FLAGS "-mtune=intel")
endif()
+ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ set(DEFAULT_VESPA_CPU_ARCH_FLAGS "-march=armv8.2-a+fp16+rcpc+dotprod+crypto -mtune=neoverse-n1")
endif()
endif()
if(DEFINED DEFAULT_CMAKE_PREFIX_PATH)
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 81a180348c2..031213ac693 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -59,6 +59,7 @@ BuildRequires: python3-devel
%if 0%{?el8}
BuildRequires: gcc-toolset-10-gcc-c++
BuildRequires: gcc-toolset-10-binutils
+BuildRequires: gcc-toolset-10-libatomic-devel
%define _devtoolset_enable /opt/rh/gcc-toolset-10/enable
BuildRequires: maven
BuildRequires: pybind11-devel
diff --git a/fastos/src/vespa/fastos/unix_thread.cpp b/fastos/src/vespa/fastos/unix_thread.cpp
index d7a0ed879c0..a45d90426ef 100644
--- a/fastos/src/vespa/fastos/unix_thread.cpp
+++ b/fastos/src/vespa/fastos/unix_thread.cpp
@@ -71,7 +71,12 @@ bool FastOS_UNIX_Thread::Initialize (int stackSize, int stackGuardSize)
#else
adjusted_stack_size += PTHREAD_STACK_MIN;
#endif
- pthread_attr_setstacksize(&attr, adjusted_stack_size);
+ if (getenv("VESPA_IGNORE_REQUESTED_STACK_SIZES") == nullptr) {
+ //fprintf(stderr, "pthread_create: using adjusted stack size %zd\n", adjusted_stack_size);
+ pthread_attr_setstacksize(&attr, adjusted_stack_size);
+ } else {
+ //fprintf(stderr, "pthread_create: ignoring requested stack size %d\n", stackSize);
+ }
rc = (0 == pthread_create(&_handle, &attr, FastOS_ThreadHook, this));
if (rc)
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
index 13b6f5d8cc3..aab2f5a53fd 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
@@ -36,7 +36,6 @@ public class FileDownloader implements AutoCloseable {
private final Supervisor supervisor;
private final File downloadDirectory;
private final Duration timeout;
- private final Duration sleepBetweenRetries;
private final FileReferenceDownloader fileReferenceDownloader;
private final Downloads downloads = new Downloads();
@@ -61,7 +60,6 @@ public class FileDownloader implements AutoCloseable {
this.supervisor = supervisor;
this.downloadDirectory = downloadDirectory;
this.timeout = timeout;
- this.sleepBetweenRetries = sleepBetweenRetries;
// Needed to receive RPC receiveFile* calls from server after starting download of file reference
new FileReceiver(supervisor, downloads, downloadDirectory);
this.fileReferenceDownloader = new FileReferenceDownloader(connectionPool,
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
index 1bb6b7586f5..7b24098526c 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
@@ -55,14 +55,20 @@ public class FileReferenceDownloader {
private void waitUntilDownloadStarted(FileReferenceDownload fileReferenceDownload) {
FileReference fileReference = fileReferenceDownload.fileReference();
int retryCount = 0;
+ Connection connection = connectionPool.getCurrent();
do {
if (FileDownloader.fileReferenceExists(fileReference, downloadDirectory))
return;
- if (startDownloadRpc(fileReferenceDownload, retryCount))
+ if (startDownloadRpc(fileReferenceDownload, retryCount, connection))
return;
try { Thread.sleep(sleepBetweenRetries.toMillis()); } catch (InterruptedException e) { /* ignored */}
retryCount++;
+
+ // There is no one connection that will always work for each file reference (each file reference might
+ // exist on just one config server, and which one could be different for each file reference), so we
+ // should get a new connection for every retry
+ connection = connectionPool.switchConnection(connection);
} while (retryCount < 5);
fileReferenceDownload.future().completeExceptionally(new RuntimeException("Failed getting " + fileReference));
@@ -84,28 +90,25 @@ public class FileReferenceDownloader {
downloads.remove(fileReference);
}
- private boolean startDownloadRpc(FileReferenceDownload fileReferenceDownload, int retryCount) {
+ private boolean startDownloadRpc(FileReferenceDownload fileReferenceDownload, int retryCount, Connection connection) {
Request request = createRequest(fileReferenceDownload);
- Connection connection = connectionPool.getCurrent();
connection.invokeSync(request, rpcTimeout(retryCount).getSeconds());
Level logLevel = (retryCount > 3 ? Level.INFO : Level.FINE);
FileReference fileReference = fileReferenceDownload.fileReference();
if (validateResponse(request)) {
- log.log(Level.FINE, () -> "Request callback, OK. Req: " + request + "\nSpec: " + connection + ", retry count " + retryCount);
+ log.log(Level.FINE, () -> "Request callback, OK. Req: " + request + "\nSpec: " + connection);
if (request.returnValues().get(0).asInt32() == 0) {
log.log(Level.FINE, () -> "Found '" + fileReference + "' available at " + connection.getAddress());
return true;
} else {
log.log(logLevel, "'" + fileReference + "' not found at " + connection.getAddress());
- connectionPool.switchConnection(connection);
return false;
}
} else {
log.log(logLevel, "Downloading " + fileReference + " from " + connection.getAddress() + " failed: " +
request + ", error: " + request.errorMessage() + ", will switch config server for next request" +
" (retry " + retryCount + ", rpc timeout " + rpcTimeout(retryCount));
- connectionPool.switchConnection(connection);
return false;
}
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 3cbdb9e1c56..b7b0c9aea30 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -308,13 +308,6 @@ public class Flags {
TENANT_ID
);
- public static final UnboundIntFlag MAX_CONNECTION_LIFE_IN_HOSTED = defineIntFlag(
- "max-connection-life-in-hosted", 45,
- List.of("bjorncs"), "2021-09-30", "2021-12-31",
- "Max connection life for connections to jdisc endpoints in hosted",
- "Takes effect at redeployment",
- APPLICATION_ID);
-
public static final UnboundBooleanFlag ENABLE_ROUTING_REUSE_PORT = defineFeatureFlag(
"enable-routing-reuse-port", false,
List.of("mortent"), "2021-09-29", "2021-12-31",
@@ -353,6 +346,13 @@ public class Flags {
"Takes effect at redeploy",
ZONE_ID, APPLICATION_ID);
+ public static final UnboundBooleanFlag UNORDERED_MERGE_CHAINING = defineFeatureFlag(
+ "unordered-merge-chaining", false,
+ List.of("vekterli", "geirst"), "2021-11-15", "2022-03-01",
+ "Enables the use of unordered merge chains for data merge operations",
+ "Takes effect at redeploy",
+ ZONE_ID, APPLICATION_ID);
+
public static final UnboundStringFlag JDK_VERSION = defineStringFlag(
"jdk-version", "11",
List.of("hmusum"), "2021-10-25", "2021-11-25",
@@ -363,6 +363,13 @@ public class Flags {
HOSTNAME,
NODE_TYPE);
+ public static final UnboundBooleanFlag IGNORE_THREAD_STACK_SIZES = defineFeatureFlag(
+ "ignore-thread-stack-sizes", false,
+ List.of("arnej"), "2021-11-12", "2022-01-31",
+ "Whether C++ thread creation should ignore any requested stack size",
+ "Triggers restart, takes effect immediately",
+ ZONE_ID, APPLICATION_ID);
+
public static final UnboundBooleanFlag USE_FILE_DISTRIBUTION_CONNECTION_POOL = defineFeatureFlag(
"use-file-distribution-connection-pool", false,
List.of("hmusum"), "2021-11-16", "2021-12-16",
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
index 92c98505cf1..3c017181bbd 100644
--- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
@@ -2,6 +2,7 @@
package com.yahoo.jdisc.http.filter.security.base;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.component.AbstractComponent;
@@ -11,10 +12,11 @@ import com.yahoo.jdisc.handler.ResponseDispatch;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
-import java.util.logging.Level;
import java.io.UncheckedIOException;
+import java.util.Objects;
import java.util.Optional;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -35,16 +37,26 @@ public abstract class JsonSecurityRequestFilterBase extends AbstractComponent im
protected abstract Optional<ErrorResponse> filter(DiscFilterRequest request);
+ protected ObjectMapper jsonMapper() { return mapper; }
+
private void writeResponse(DiscFilterRequest request, ErrorResponse error, ResponseHandler responseHandler) {
- ObjectNode errorMessage = mapper.createObjectNode();
- errorMessage.put("code", error.errorCode);
- errorMessage.put("message", error.message);
+ JsonNode json;
+ if (error.customJson != null) {
+ json = error.customJson;
+ } else {
+ ObjectNode o = mapper.createObjectNode();
+ if (error.errorCodeAsInt != null) o.put("code", error.errorCodeAsInt);
+ else if (error.errorCodeAsString != null) o.put("code", error.errorCodeAsString);
+ if (error.message != null) o.put("message", error.message);
+ json = o;
+ }
error.response.headers().put("Content-Type", "application/json"); // Note: Overwrites header if already exists
error.response.headers().put("Cache-Control", "must-revalidate,no-cache,no-store");
- log.log(Level.FINE, () -> String.format("Error response for '%s': statusCode=%d, errorCode=%d, message='%s'",
- request, error.response.getStatus(), error.errorCode, error.message));
try (FastContentWriter writer = ResponseDispatch.newInstance(error.response).connectFastWriter(responseHandler)) {
- writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(errorMessage));
+ String jsonAsStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
+ log.log(Level.FINE, () -> String.format("Error response for '%s': statusCode=%d, json='%s'",
+ request, error.response.getStatus(), jsonAsStr));
+ writer.write(jsonAsStr);
} catch (JsonProcessingException e) {
throw new UncheckedIOException(e);
}
@@ -56,15 +68,24 @@ public abstract class JsonSecurityRequestFilterBase extends AbstractComponent im
*/
protected static class ErrorResponse {
private final Response response;
- private final int errorCode;
+ private final JsonNode customJson;
+ private final Integer errorCodeAsInt;
+ private final String errorCodeAsString;
private final String message;
- public ErrorResponse(Response response, int errorCode, String message) {
- this.response = response;
- this.errorCode = errorCode;
+ private ErrorResponse(Response response, JsonNode customJson, Integer errorCodeAsInt, String errorCodeAsString,
+ String message) {
+ this.response = Objects.requireNonNull(response);
+ this.customJson = customJson;
+ this.errorCodeAsInt = errorCodeAsInt;
+ this.errorCodeAsString = errorCodeAsString;
this.message = message;
}
+ public ErrorResponse(Response response, int errorCode, String message) {
+ this(response, null, errorCode, null, message);
+ }
+
public ErrorResponse(Response response, String message) {
this(response, response.getStatus(), message);
}
@@ -73,21 +94,23 @@ public abstract class JsonSecurityRequestFilterBase extends AbstractComponent im
this(new Response(httpStatusCode), errorCode, message);
}
+ public ErrorResponse(int httpStatusCode, String errorCode, String message) {
+ this(new Response(httpStatusCode), null, null, errorCode, message);
+ }
+
public ErrorResponse(int httpStatusCode, String message) {
this(new Response(httpStatusCode), message);
}
- public Response getResponse() {
- return response;
+ public ErrorResponse(Response response, JsonNode json) {
+ this(response, json, null, null, null);
}
- public int getErrorCode() {
- return errorCode;
+ public Response getResponse() {
+ return response;
}
- public String getMessage() {
- return message;
- }
+ public Optional<String> getMessage() { return Optional.ofNullable(message); }
}
}
diff --git a/searchcore/src/tests/proton/attribute/attribute_test.cpp b/searchcore/src/tests/proton/attribute/attribute_test.cpp
index 2151556b89d..d4ad85d88d9 100644
--- a/searchcore/src/tests/proton/attribute/attribute_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_test.cpp
@@ -42,9 +42,11 @@
#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/test/insertion_operators.h>
+#include <vespa/vespalib/util/destructor_callbacks.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/foreground_thread_executor.h>
#include <vespa/vespalib/util/foregroundtaskexecutor.h>
+#include <vespa/vespalib/util/gate.h>
#include <vespa/vespalib/util/sequencedtaskexecutorobserver.h>
#include <vespa/log/log.h>
@@ -86,6 +88,7 @@ using vespalib::eval::SimpleValue;
using vespalib::eval::TensorSpec;
using vespalib::eval::Value;
using vespalib::eval::ValueType;
+using vespalib::GateCallback;
using vespalib::IDestructorCallback;
using AVBasicType = search::attribute::BasicType;
@@ -199,8 +202,9 @@ public:
EXPECT_EQ(includeCommit, _attributeFieldWriter->getExecuteHistory());
}
SerialNum test_force_commit(AttributeVector &attr, SerialNum serialNum) {
- commit(serialNum);
- _attributeFieldWriter->sync_all();
+ vespalib::Gate gate;
+ _aw->forceCommit(serialNum, std::make_shared<GateCallback>(gate));
+ gate.await();
return attr.getStatus().getLastSyncToken();
}
};
diff --git a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
index e20c4268f88..b2acc8703f3 100644
--- a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
@@ -287,7 +287,7 @@ struct FastAccessFixture
vespalib::mkdir(BASE_DIR);
}
~FastAccessFixture() {
- _writeService.sync();
+ _writeService.sync_all_executors();
}
};
diff --git a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
index 5e42231d866..091292b3151 100644
--- a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
@@ -313,9 +313,9 @@ struct FixtureBase
init();
}
~FixtureBase() {
- _writeService.sync();
+ _writeService.sync_all_executors();
_writeService.master().execute(makeLambdaTask([this]() { _subDb.close(); }));
- _writeService.sync();
+ _writeService.sync_all_executors();
}
template <typename FunctionType>
void runInMaster(FunctionType func) {
@@ -591,20 +591,20 @@ TEST_F("require that attribute compaction config reflect retirement", FastAccess
auto calc = std::make_shared<proton::test::BucketStateCalculator>();
calc->setNodeRetired(true);
f._subDb.setBucketStateCalculator(calc);
- f._writeService.sync();
+ f._writeService.sync_all_executors();
guard = f._subDb.getAttributeManager()->getAttribute("attr1");
EXPECT_EQUAL(retired_cfg, (*guard)->getConfig().getCompactionStrategy());
EXPECT_EQUAL(retired_cfg, dynamic_cast<const proton::DocumentMetaStore &>(f._subDb.getDocumentMetaStoreContext().get()).getConfig().getCompactionStrategy());
f.basicReconfig(10);
- f._writeService.sync();
+ f._writeService.sync_all_executors();
guard = f._subDb.getAttributeManager()->getAttribute("attr1");
EXPECT_EQUAL(retired_cfg, (*guard)->getConfig().getCompactionStrategy());
EXPECT_EQUAL(retired_cfg, dynamic_cast<const proton::DocumentMetaStore &>(f._subDb.getDocumentMetaStoreContext().get()).getConfig().getCompactionStrategy());
calc->setNodeRetired(false);
f._subDb.setBucketStateCalculator(calc);
- f._writeService.sync();
+ f._writeService.sync_all_executors();
guard = f._subDb.getAttributeManager()->getAttribute("attr1");
EXPECT_EQUAL(default_cfg, (*guard)->getConfig().getCompactionStrategy());
EXPECT_EQUAL(default_cfg, dynamic_cast<const proton::DocumentMetaStore &>(f._subDb.getDocumentMetaStoreContext().get()).getConfig().getCompactionStrategy());
diff --git a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
index 5384a985af0..977c899ab11 100644
--- a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
@@ -455,7 +455,7 @@ struct FeedHandlerFixture
}
~FeedHandlerFixture() {
- writeService.sync();
+ writeService.sync_all_executors();
}
template <class FunctionType>
inline void runAsMaster(FunctionType &&function) {
diff --git a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
index 97faa81b48a..e53468e0dd4 100644
--- a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
@@ -516,11 +516,11 @@ struct FixtureBase
}
void syncIndex() {
- _writeService.sync();
+ _writeService.sync_all_executors();
}
void sync() {
- _writeServiceReal.sync();
+ _writeServiceReal.sync_all_executors();
}
const test::DocumentMetaStoreObserver &metaStoreObserver() {
@@ -701,7 +701,7 @@ FixtureBase::FixtureBase()
}
FixtureBase::~FixtureBase() {
- _writeServiceReal.sync();
+ _writeServiceReal.sync_all_executors();
}
void
diff --git a/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp b/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
index 5146a16272a..eb398b9ee48 100644
--- a/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/storeonlyfeedview/storeonlyfeedview_test.cpp
@@ -246,7 +246,7 @@ struct FixtureBase {
void force_commit() {
runInMaster([this] () { static_cast<IFeedView&>(*feedview).forceCommit(serial_num); });
- writeService.sync();
+ writeService.sync_all_executors();
}
};
diff --git a/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp b/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
index c49ec67f220..9162972a4cb 100644
--- a/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
+++ b/searchcore/src/tests/proton/documentmetastore/lidreusedelayer/lidreusedelayer_test.cpp
@@ -185,7 +185,7 @@ public:
runInMaster([&] () { cycleLids(_lidReuseDelayer->getReuseLids()); });
}
- void sync() { _writeService.sync(); }
+ void sync() { _writeService.sync_all_executors(); }
};
diff --git a/searchcore/src/tests/proton/index/indexmanager_test.cpp b/searchcore/src/tests/proton/index/indexmanager_test.cpp
index 4c442d38443..d34e2ae667e 100644
--- a/searchcore/src/tests/proton/index/indexmanager_test.cpp
+++ b/searchcore/src/tests/proton/index/indexmanager_test.cpp
@@ -128,7 +128,7 @@ struct IndexManagerTest : public ::testing::Test {
{
removeTestData();
vespalib::mkdir(index_dir, false);
- _writeService.sync();
+ _writeService.sync_all_executors();
resetIndexManager();
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
index 587ec8dda1f..f3d90d37e42 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_writer.cpp
@@ -14,6 +14,8 @@
#include <vespa/searchlib/attribute/imported_attribute_vector.h>
#include <vespa/searchlib/tensor/prepare_result.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
+#include <vespa/vespalib/util/destructor_callbacks.h>
+#include <vespa/vespalib/util/gate.h>
#include <vespa/vespalib/util/idestructorcallback.h>
#include <vespa/vespalib/util/threadexecutor.h>
#include <future>
@@ -27,6 +29,7 @@ using namespace search;
using ExecutorId = vespalib::ISequencedTaskExecutor::ExecutorId;
using search::attribute::ImportedAttributeVector;
using search::tensor::PrepareResult;
+using vespalib::GateCallback;
using vespalib::ISequencedTaskExecutor;
namespace proton {
@@ -821,24 +824,38 @@ AttributeWriter::forceCommit(const CommitParam & param, OnWriteDoneType onWriteD
void
AttributeWriter::onReplayDone(uint32_t docIdLimit)
{
- for (auto entry : _attrMap) {
- _attributeFieldWriter.execute(entry.second.executor_id,
- [docIdLimit, attr = entry.second.attribute]()
- { applyReplayDone(docIdLimit, *attr); });
+ vespalib::Gate gate;
+ {
+ auto on_write_done = std::make_shared<GateCallback>(gate);
+ for (auto entry : _attrMap) {
+ _attributeFieldWriter.execute(entry.second.executor_id,
+ [docIdLimit, attr = entry.second.attribute, on_write_done]()
+ {
+ (void) on_write_done;
+ applyReplayDone(docIdLimit, *attr);
+ });
+ }
}
- _attributeFieldWriter.sync_all();
+ gate.await();
}
void
AttributeWriter::compactLidSpace(uint32_t wantedLidLimit, SerialNum serialNum)
{
- for (auto entry : _attrMap) {
- _attributeFieldWriter.execute(entry.second.executor_id,
- [wantedLidLimit, serialNum, attr=entry.second.attribute]()
- { applyCompactLidSpace(wantedLidLimit, serialNum, *attr); });
+ vespalib::Gate gate;
+ {
+ auto on_write_done = std::make_shared<GateCallback>(gate);
+ for (auto entry : _attrMap) {
+ _attributeFieldWriter.execute(entry.second.executor_id,
+ [wantedLidLimit, serialNum, attr=entry.second.attribute, on_write_done]()
+ {
+ (void) on_write_done;
+ applyCompactLidSpace(wantedLidLimit, serialNum, *attr);
+ });
+ }
}
- _attributeFieldWriter.sync_all();
+ gate.await();
}
bool
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
index 645c9b15f07..3f2fb6c4634 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
@@ -62,6 +62,8 @@ using storage::spi::Timestamp;
using search::common::FileHeaderContext;
using proton::initializer::InitializerTask;
using proton::initializer::TaskRunner;
+using vespalib::GateCallback;
+using vespalib::IDestructorCallback;
using vespalib::makeLambdaTask;
using searchcorespi::IFlushTarget;
@@ -186,7 +188,6 @@ DocumentDB::DocumentDB(const vespalib::string &baseDir,
_metricsHook(std::make_unique<MetricsUpdateHook>(*this)),
_feedView(),
_refCount(),
- _syncFeedViewEnabled(false),
_owner(owner),
_bucketExecutor(bucketExecutor),
_state(),
@@ -313,9 +314,9 @@ void
DocumentDB::initFinish(DocumentDBConfig::SP configSnapshot)
{
// Called by executor thread
+ assert(_writeService.master().isCurrentThread());
_bucketHandler.setReadyBucketHandler(_subDBs.getReadySubDB()->getDocumentMetaStoreContext().get());
_subDBs.initViews(*configSnapshot, _sessionManager);
- _syncFeedViewEnabled = true;
syncFeedView();
// Check that feed view has been activated.
assert(_feedView.get());
@@ -376,9 +377,13 @@ void
DocumentDB::enterOnlineState()
{
// Called by executor thread
- // Ensure that all replayed operations are committed to memory structures
- _feedView.get()->forceCommit(CommitParam(_feedHandler->getSerialNum()));
- _writeService.sync();
+ assert(_writeService.master().isCurrentThread());
+ {
+ vespalib::Gate gate;
+ // Ensure that all replayed operations are committed to memory structures
+ _feedView.get()->forceCommit(CommitParam(_feedHandler->getSerialNum()), std::make_shared<GateCallback>(gate));
+ gate.await();
+ }
(void) _state.enterOnlineState();
// Consider delayed pruning of transaction log and config history
@@ -464,10 +469,11 @@ DocumentDB::applyConfig(DocumentDBConfig::SP configSnapshot, SerialNum serialNum
}
{
bool elidedConfigSave = equalReplayConfig && tlsReplayDone;
+ vespalib::Gate gate;
// Flush changes to attributes and memory index, cf. visibilityDelay
_feedView.get()->forceCommit(CommitParam(elidedConfigSave ? serialNum : serialNum - 1),
- std::make_shared<vespalib::KeepAlive<FeedHandler::CommitResult>>(std::move(commit_result)));
- _writeService.sync();
+ std::make_shared<vespalib::KeepAlive<std::pair<FeedHandler::CommitResult, std::shared_ptr<IDestructorCallback>>>>(std::make_pair(std::move(commit_result), std::make_shared<GateCallback>(gate))));
+ gate.await();
}
if (params.shouldMaintenanceControllerChange()) {
_maintenanceController.killJobs();
@@ -510,20 +516,11 @@ DocumentDB::applyConfig(DocumentDBConfig::SP configSnapshot, SerialNum serialNum
}
-namespace {
-void
-doNothing(IFeedView::SP)
-{
- // Called by index executor, delays when feed view is dropped.
-}
-} // namespace
-
void
DocumentDB::performDropFeedView(IFeedView::SP feedView)
{
- // Called by executor task, delays when feed view is dropped.
- // Also called by DocumentDB::receive() method to keep feed view alive
-
+ // Delays when feed view is dropped.
+ assert(_writeService.master().isCurrentThread());
_writeService.attributeFieldWriter().sync_all();
_writeService.summary().sync();
@@ -534,11 +531,11 @@ DocumentDB::performDropFeedView(IFeedView::SP feedView)
void
DocumentDB::performDropFeedView2(IFeedView::SP feedView) {
- // Called by executor task, delays when feed view is dropped.
- // Also called by DocumentDB::receive() method to keep feed view alive
+ // Delays when feed view is dropped.
+ assert(_writeService.index().isCurrentThread());
_writeService.indexFieldInverter().sync_all();
_writeService.indexFieldWriter().sync_all();
- masterExecute([feedView]() { doNothing(feedView); });
+ masterExecute([feedView]() { (void) feedView; });
}
@@ -575,15 +572,15 @@ DocumentDB::close()
// Caller should have removed document DB from feed router.
_refCount.waitForZeroRefCount();
- _writeService.sync();
+ _writeService.sync_all_executors();
// The attributes in the ready sub db is also the total set of attributes.
DocumentDBTaggedMetrics &metrics = getMetrics();
_metricsWireService.cleanAttributes(metrics.ready.attributes);
_metricsWireService.cleanAttributes(metrics.notReady.attributes);
- _writeService.sync();
+ _writeService.sync_all_executors();
masterExecute([this] () { closeSubDBs(); } );
- _writeService.sync();
+ _writeService.sync_all_executors();
// What about queued tasks ?
_writeService.shutdown();
_maintenanceController.kill();
@@ -912,15 +909,12 @@ DocumentDB::getActiveGeneration() const {
void
DocumentDB::syncFeedView()
{
- // Called by executor or while in rendezvous with executor
-
- if (!_syncFeedViewEnabled)
- return;
+ assert(_writeService.master().isCurrentThread());
IFeedView::SP oldFeedView(_feedView.get());
IFeedView::SP newFeedView(_subDBs.getFeedView());
_maintenanceController.killJobs();
- _writeService.sync();
+ _writeService.sync_all_executors();
_feedView.set(newFeedView);
_feedHandler->setActiveFeedView(newFeedView.get());
@@ -994,7 +988,7 @@ void
DocumentDB::stopMaintenance()
{
_maintenanceController.stop();
- _writeService.sync();
+ _writeService.sync_all_executors();
}
void
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.h b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
index 014bba11f83..6b855cd40a8 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
@@ -112,7 +112,6 @@ private:
std::unique_ptr<metrics::UpdateHook> _metricsHook;
vespalib::VarHolder<IFeedView::SP> _feedView;
vespalib::MonitoredRefCount _refCount;
- bool _syncFeedViewEnabled;
IDocumentDBOwner &_owner;
storage::spi::BucketExecutor &_bucketExecutor;
DDBState _state;
diff --git a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
index d35aaf9f909..0e9ba7a24c8 100644
--- a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
@@ -88,13 +88,12 @@ ExecutorThreadingService::ExecutorThreadingService(vespalib::ThreadExecutor & sh
ExecutorThreadingService::~ExecutorThreadingService() = default;
-vespalib::Syncable &
-ExecutorThreadingService::sync() {
+void
+ExecutorThreadingService::sync_all_executors() {
// We have multiple patterns where task A posts to B which post back to A
for (size_t i = 0; i < 2; i++) {
syncOnce();
}
- return *this;
}
void
diff --git a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
index 51da27586f7..e571e205f47 100644
--- a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
+++ b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
@@ -17,7 +17,7 @@ class ThreadingServiceConfig;
class ExecutorThreadingService : public searchcorespi::index::IThreadingService
{
private:
- vespalib::ThreadExecutor & _sharedExecutor;
+ vespalib::ThreadExecutor & _sharedExecutor;
vespalib::ThreadStackExecutor _masterExecutor;
std::unique_ptr<vespalib::SyncableThreadExecutor> _indexExecutor;
std::unique_ptr<vespalib::SyncableThreadExecutor> _summaryExecutor;
@@ -46,10 +46,7 @@ public:
ExecutorThreadingService(vespalib::ThreadExecutor &sharedExecutor, uint32_t num_treads = 1);
~ExecutorThreadingService() override;
- /**
- * Implements vespalib::Syncable
- */
- vespalib::Syncable &sync() override;
+ void sync_all_executors() override;
void shutdown();
@@ -67,9 +64,6 @@ public:
return *_summaryExecutor;
}
- /**
- * Implements IThreadingService
- */
searchcorespi::index::IThreadService &master() override {
return _masterService;
}
@@ -90,6 +84,6 @@ public:
ExecutorThreadingServiceStats getStats();
};
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
index 8451f3268b8..40a1a1a45f3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
@@ -17,6 +17,8 @@
#include <vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.h>
#include <vespa/searchcore/proton/reprocessing/reprocess_documents_task.h>
#include <vespa/searchlib/docstore/document_store_visitor_progress.h>
+#include <vespa/vespalib/util/destructor_callbacks.h>
+
#include <vespa/log/log.h>
LOG_SETUP(".proton.server.fast_access_doc_subdb");
@@ -314,9 +316,13 @@ FastAccessDocSubDB::onReprocessDone(SerialNum serialNum)
{
IFeedView::SP feedView = _iFeedView.get();
IAttributeWriter::SP attrWriter = static_cast<FastAccessFeedView &>(*feedView).getAttributeWriter();
- attrWriter->forceCommit(serialNum, std::shared_ptr<vespalib::IDestructorCallback>());
- _writeService.attributeFieldWriter().sync_all();
- _writeService.summary().sync();
+ vespalib::Gate gate;
+ {
+ auto onDone = std::make_shared<vespalib::GateCallback>(gate);
+ attrWriter->forceCommit(serialNum, onDone);
+ _writeService.summary().execute(vespalib::makeLambdaTask([done = std::move(onDone)]() { (void) done; }));
+ }
+ gate.await();
Parent::onReprocessDone(serialNum);
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp b/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
index ce7f1d70195..db2bb7ed2cb 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fast_access_feed_view.cpp
@@ -73,7 +73,7 @@ void
FastAccessFeedView::handleCompactLidSpace(const CompactLidSpaceOperation &op)
{
// Drain pending PutDoneContext and ForceCommitContext objects
- _writeService.sync();
+ _writeService.sync_all_executors();
_docIdLimit.set(op.getLidLimit());
getAttributeWriter()->compactLidSpace(op.getLidLimit(), op.getSerialNum());
Parent::handleCompactLidSpace(op);
diff --git a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
index af746f9debb..c9294150f16 100644
--- a/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/feedhandler.cpp
@@ -303,7 +303,7 @@ void
FeedHandler::performEof()
{
assert(_writeService.master().isCurrentThread());
- _writeService.sync();
+ _writeService.sync_all_executors();
LOG(debug, "Visiting done for transaction log domain '%s', eof received", _tlsMgr.getDomainName().c_str());
// Replay must be complete
if (_replay_end_serial_num != _serialNum) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
index b512143c5fd..bf3589457f9 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
@@ -244,7 +244,7 @@ SearchableDocSubDB::reconfigure(std::unique_ptr<Configure> configure)
{
assert(_writeService.master().isCurrentThread());
- _writeService.sync();
+ _writeService.sync_all_executors();
// Everything should be quiet now.
diff --git a/searchcore/src/vespa/searchcore/proton/test/thread_utils.h b/searchcore/src/vespa/searchcore/proton/test/thread_utils.h
index 84b7c12cba6..6b08eecf61f 100644
--- a/searchcore/src/vespa/searchcore/proton/test/thread_utils.h
+++ b/searchcore/src/vespa/searchcore/proton/test/thread_utils.h
@@ -14,7 +14,7 @@ void
runInMaster(searchcorespi::index::IThreadingService &writeService, FunctionType func)
{
writeService.master().execute(vespalib::makeLambdaTask(std::move(func)));
- writeService.sync();
+ writeService.sync_all_executors();
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/test/threading_service_observer.h b/searchcore/src/vespa/searchcore/proton/test/threading_service_observer.h
index 94b62962f04..1d379f439fa 100644
--- a/searchcore/src/vespa/searchcore/proton/test/threading_service_observer.h
+++ b/searchcore/src/vespa/searchcore/proton/test/threading_service_observer.h
@@ -32,19 +32,9 @@ public:
const ThreadServiceObserver &summaryObserver() const {
return _summary;
}
- const vespalib::SequencedTaskExecutorObserver &indexFieldInverterObserver() const {
- return _indexFieldInverter;
- }
- const vespalib::SequencedTaskExecutorObserver &indexFieldWriterObserver() const {
- return _indexFieldWriter;
- }
-
- const vespalib::SequencedTaskExecutorObserver &attributeFieldWriterObserver() const {
- return _attributeFieldWriter;
- }
- vespalib::Syncable &sync() override {
- return _service.sync();
+ void sync_all_executors() override {
+ _service.sync_all_executors();
}
searchcorespi::index::IThreadService &master() override {
diff --git a/searchcorespi/src/vespa/searchcorespi/index/ithreadingservice.h b/searchcorespi/src/vespa/searchcorespi/index/ithreadingservice.h
index be8c9ef7d86..f30aec94d53 100644
--- a/searchcorespi/src/vespa/searchcorespi/index/ithreadingservice.h
+++ b/searchcorespi/src/vespa/searchcorespi/index/ithreadingservice.h
@@ -2,7 +2,6 @@
#pragma once
#include "i_thread_service.h"
-#include <vespa/vespalib/util/syncable.h>
namespace vespalib { class ISequencedTaskExecutor; }
namespace searchcorespi::index {
@@ -57,13 +56,15 @@ namespace searchcorespi::index {
* TODO: * indexFieldInverter and indexFieldWriter can be collapsed to one. Both need sequencing,
* but they sequence on different things so efficiency will be the same and just depends on #threads
*/
-struct IThreadingService : public vespalib::Syncable
+struct IThreadingService
{
IThreadingService(const IThreadingService &) = delete;
IThreadingService & operator = (const IThreadingService &) = delete;
IThreadingService() = default;
virtual ~IThreadingService() = default;
+ virtual void sync_all_executors() = 0;
+
virtual IThreadService &master() = 0;
virtual IThreadService &index() = 0;
virtual IThreadService &summary() = 0;
diff --git a/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp b/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
index da6cfdba158..2a12867dd33 100644
--- a/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
+++ b/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
@@ -182,31 +182,31 @@ struct Compiler : public Blueprint::DependencyHandler {
}
FeatureRef resolve_feature(const vespalib::string &feature_name, Accept accept_type) {
- FeatureNameParser parser(feature_name);
- if (!parser.valid()) {
+ auto parser = std::make_unique<FeatureNameParser>(feature_name);
+ if (!parser->valid()) {
return fail(feature_name, "malformed name");
}
- if (failed_set.count(parser.featureName()) > 0) {
- return fail(parser.featureName(), "already failed");
+ if (failed_set.count(parser->featureName()) > 0) {
+ return fail(parser->featureName(), "already failed");
}
- auto old_feature = feature_map.find(parser.featureName());
+ auto old_feature = feature_map.find(parser->featureName());
if (old_feature != feature_map.end()) {
- return verify_type(parser, old_feature->second, accept_type);
+ return verify_type(*parser, old_feature->second, accept_type);
}
if ((resolve_stack.size() + 1) > BlueprintResolver::MAX_DEP_DEPTH) {
- return fail(parser.featureName(), "dependency graph too deep");
+ return fail(parser->featureName(), "dependency graph too deep");
}
for (const Frame &frame: resolve_stack) {
- if (frame.parser.executorName() == parser.executorName()) {
- return fail(parser.featureName(), "dependency cycle detected");
+ if (frame.parser.executorName() == parser->executorName()) {
+ return fail(parser->featureName(), "dependency cycle detected");
}
}
- setup_executor(parser);
- auto new_feature = feature_map.find(parser.featureName());
+ setup_executor(*parser);
+ auto new_feature = feature_map.find(parser->featureName());
if (new_feature != feature_map.end()) {
- return verify_type(parser, new_feature->second, accept_type);
+ return verify_type(*parser, new_feature->second, accept_type);
}
- return fail(parser.featureName(), fmt("unknown output: '%s'", parser.output().c_str()));
+ return fail(parser->featureName(), fmt("unknown output: '%s'", parser->output().c_str()));
}
std::optional<FeatureType> resolve_input(const vespalib::string &feature_name, Accept accept_type) override {
diff --git a/staging_vespalib/src/vespa/vespalib/util/sequencedtaskexecutor.h b/staging_vespalib/src/vespa/vespalib/util/sequencedtaskexecutor.h
index 06e7fa65ac2..7bb56424849 100644
--- a/staging_vespalib/src/vespa/vespalib/util/sequencedtaskexecutor.h
+++ b/staging_vespalib/src/vespa/vespalib/util/sequencedtaskexecutor.h
@@ -4,6 +4,7 @@
#include "isequencedtaskexecutor.h"
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/util/runnable.h>
+#include <optional>
namespace vespalib {
diff --git a/storage/src/vespa/storage/distributor/node_supported_features_repo.cpp b/storage/src/vespa/storage/distributor/node_supported_features_repo.cpp
index e125f360cec..2e5335c012a 100644
--- a/storage/src/vespa/storage/distributor/node_supported_features_repo.cpp
+++ b/storage/src/vespa/storage/distributor/node_supported_features_repo.cpp
@@ -9,7 +9,7 @@ NodeSupportedFeaturesRepo::NodeSupportedFeaturesRepo() = default;
NodeSupportedFeaturesRepo::NodeSupportedFeaturesRepo(
vespalib::hash_map<uint16_t, NodeSupportedFeatures> features,
- PrivateCtorTag)
+ PrivateCtorTag) noexcept
: _node_features(std::move(features))
{}
diff --git a/storage/src/vespa/storage/distributor/node_supported_features_repo.h b/storage/src/vespa/storage/distributor/node_supported_features_repo.h
index cc40c27b8e2..2167858ad28 100644
--- a/storage/src/vespa/storage/distributor/node_supported_features_repo.h
+++ b/storage/src/vespa/storage/distributor/node_supported_features_repo.h
@@ -20,7 +20,7 @@ class NodeSupportedFeaturesRepo {
public:
NodeSupportedFeaturesRepo();
- NodeSupportedFeaturesRepo(vespalib::hash_map<uint16_t, NodeSupportedFeatures> features, PrivateCtorTag);
+ NodeSupportedFeaturesRepo(vespalib::hash_map<uint16_t, NodeSupportedFeatures> features, PrivateCtorTag) noexcept;
~NodeSupportedFeaturesRepo();
// Returns supported node features for node with distribution key node_idx, or a default feature set
diff --git a/storage/src/vespa/storage/persistence/processallhandler.cpp b/storage/src/vespa/storage/persistence/processallhandler.cpp
index 9308ae2a807..04afdc9eb85 100644
--- a/storage/src/vespa/storage/persistence/processallhandler.cpp
+++ b/storage/src/vespa/storage/persistence/processallhandler.cpp
@@ -86,7 +86,7 @@ ProcessAllHandler::handleRemoveLocation(api::RemoveLocationCommand& cmd, Message
spi::Bucket bucket(cmd.getBucket());
UnrevertableRemoveEntryProcessor processor(_spi, bucket, tracker->context());
BucketProcessor::iterateAll(_spi, bucket, cmd.getDocumentSelection(),
- std::make_shared<document::AllFields>(),
+ std::make_shared<document::DocIdOnly>(),
processor, spi::NEWEST_DOCUMENT_ONLY,tracker->context());
tracker->setReply(std::make_shared<api::RemoveLocationReply>(cmd, processor._n_removed));
@@ -104,7 +104,7 @@ ProcessAllHandler::handleStatBucket(api::StatBucketCommand& cmd, MessageTracker:
spi::Bucket bucket(cmd.getBucket());
StatEntryProcessor processor(ost);
BucketProcessor::iterateAll(_spi, bucket, cmd.getDocumentSelection(),
- std::make_shared<document::AllFields>(),
+ std::make_shared<document::DocIdOnly>(),
processor, spi::ALL_VERSIONS,tracker->context());
tracker->setReply(std::make_shared<api::StatBucketReply>(cmd, ost.str()));
diff --git a/vespabase/src/rhel-prestart.sh b/vespabase/src/rhel-prestart.sh
index 395a3e3d91d..dbc77879efe 100755
--- a/vespabase/src/rhel-prestart.sh
+++ b/vespabase/src/rhel-prestart.sh
@@ -78,6 +78,9 @@ findhost
if [ "$VESPA_USER" = "" ]; then
VESPA_USER=$(id -run)
fi
+if [ "$VESPA_GROUP" = "" ]; then
+ VESPA_GROUP=$(id -rgn)
+fi
cd $VESPA_HOME || { echo "Cannot cd to $VESPA_HOME" 1>&2; exit 1; }
@@ -96,32 +99,32 @@ fixdir () {
# BEGIN directory fixups
-fixdir root root 1777 logs
-fixdir root root 1777 tmp
-fixdir root root 1777 var/run
-fixdir ${VESPA_USER} root 1777 var/crash
-fixdir ${VESPA_USER} root 1777 logs/vespa
-fixdir ${VESPA_USER} root 1777 tmp/vespa
-fixdir root root 755 var
-fixdir ${VESPA_USER} root 755 libexec/vespa/plugins/qrs
-fixdir ${VESPA_USER} root 755 logs/vespa/configserver
-fixdir ${VESPA_USER} root 755 logs/vespa/qrs
-fixdir ${VESPA_USER} root 755 logs/vespa/search
-fixdir ${VESPA_USER} root 755 var/db/vespa
-fixdir ${VESPA_USER} root 755 var/db/vespa/tmp
-fixdir ${VESPA_USER} root 755 var/db/vespa/config_server
-fixdir ${VESPA_USER} root 755 var/db/vespa/config_server/serverdb
-fixdir ${VESPA_USER} root 755 var/db/vespa/config_server/serverdb/tenants
-fixdir ${VESPA_USER} root 755 var/db/vespa/filedistribution
-fixdir ${VESPA_USER} root 755 var/db/vespa/index
-fixdir ${VESPA_USER} root 755 var/db/vespa/logcontrol
-fixdir ${VESPA_USER} root 755 var/db/vespa/search
-fixdir ${VESPA_USER} root 755 var/jdisc_container
-fixdir ${VESPA_USER} root 755 var/vespa
-fixdir ${VESPA_USER} root 755 var/vespa/application
-fixdir ${VESPA_USER} root 755 var/vespa/bundlecache
-fixdir ${VESPA_USER} root 755 var/vespa/bundlecache/configserver
-fixdir ${VESPA_USER} root 755 var/vespa/cache/config/
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 libexec/vespa/plugins/qrs
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 logs
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 logs/vespa
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 logs/vespa/configserver
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 logs/vespa/qrs
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 logs/vespa/search
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 tmp
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 tmp/vespa
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/crash
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/config_server
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/config_server/serverdb
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/config_server/serverdb/tenants
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/filedistribution
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/index
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/logcontrol
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/search
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/db/vespa/tmp
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/jdisc_container
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/run
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/vespa
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/vespa/application
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/vespa/bundlecache
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/vespa/bundlecache/configserver
+fixdir ${VESPA_USER} ${VESPA_GROUP} 755 var/vespa/cache/config/
if [ "${VESPA_UNPRIVILEGED}" != yes ]; then
chown -hR ${VESPA_USER} logs/vespa
diff --git a/vespamalloc/src/tests/thread/thread_test.sh b/vespamalloc/src/tests/thread/thread_test.sh
index 45734bab3a7..cf92f8eb0fc 100755
--- a/vespamalloc/src/tests/thread/thread_test.sh
+++ b/vespamalloc/src/tests/thread/thread_test.sh
@@ -4,8 +4,8 @@ set -e
echo "Trying to find limit for processes:"
if ulimit -u; then
- echo "Fixing limit to 31100"
- ulimit -u 31100
+ echo "Fixing limit to 14100"
+ ulimit -u 14100
elif [ "$RETRYEXEC" ]; then
echo "Already tried to re-exec script, giving up."
exit 1
diff --git a/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt b/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
index 9e6e235f6b4..b7871cfbde1 100644
--- a/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
+++ b/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
@@ -1,4 +1,4 @@
# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install_fat_java_artifact(zookeeper-server-3.6.3)
# Needs to be included when this is the wanted default version (and symlinks for other versions need to be removed)
-install_symlink(lib/jars/zookeeper-server-3.6.3-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
+#install_symlink(lib/jars/zookeeper-server-3.6.3-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
diff --git a/zookeeper-server/zookeeper-server-3.7.0/CMakeLists.txt b/zookeeper-server/zookeeper-server-3.7.0/CMakeLists.txt
index c106a26fffc..8a41fc8b8fa 100644
--- a/zookeeper-server/zookeeper-server-3.7.0/CMakeLists.txt
+++ b/zookeeper-server/zookeeper-server-3.7.0/CMakeLists.txt
@@ -1,4 +1,4 @@
# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install_fat_java_artifact(zookeeper-server-3.7.0)
# Needs to be included when this is the wanted default version (and symlinks for other versions need to be removed)
-#install_symlink(lib/jars/zookeeper-server-3.7.0-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
+install_symlink(lib/jars/zookeeper-server-3.7.0-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)