Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/state-svc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/ActiveState/cli/internal/rollbar"
"github.com/ActiveState/cli/internal/runbits/panics"
"github.com/ActiveState/cli/internal/svcctl"
"github.com/ActiveState/cli/pkg/platform/api"
"github.com/ActiveState/cli/pkg/platform/authentication"
"github.com/inconshreveable/mousetrap"
)
Expand Down Expand Up @@ -70,6 +71,8 @@ func main() {
rollbar.SetupRollbar(constants.StateServiceRollbarToken)
rollbar.SetConfig(cfg)

api.SetConfig(cfg)

if os.Getenv("VERBOSE") == "true" {
logging.CurrentHandler().SetVerbose(true)
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/state/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/ActiveState/cli/internal/runbits/panics"
"github.com/ActiveState/cli/internal/subshell"
"github.com/ActiveState/cli/internal/svcctl"
"github.com/ActiveState/cli/pkg/platform/api"
secretsapi "github.com/ActiveState/cli/pkg/platform/api/secrets"
"github.com/ActiveState/cli/pkg/platform/authentication"
"github.com/ActiveState/cli/pkg/platform/model"
Expand Down Expand Up @@ -91,6 +92,7 @@ func main() {
return
}
rollbar.SetConfig(cfg)
api.SetConfig(cfg)

// Configuration options
// This should only be used if the config option is not exclusive to one package.
Expand Down
6 changes: 6 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ const VulnerabilitiesAPIPath = "/v13s/v1/graphql"
// HasuraInventoryAPIPath is the path used for the hasura inventory api
const HasuraInventoryAPIPath = "/sv/hasura-inventory/v1/graphql"

// UpdateInfoAPIPath is the path used for the update info api
const UpdateInfoAPIPath = "/sv/state-update/api/v1"

// NotificationsInfoURL is the URL we check against to see what versions are deprecated
const NotificationsInfoURL = "https://state-tool.s3.amazonaws.com/messages.json"

Expand Down Expand Up @@ -409,6 +412,9 @@ const SecurityPromptConfig = "security.prompt.enabled"
// SecurityPromptLevelConfig is the config key used to determine the level of security prompts
const SecurityPromptLevelConfig = "security.prompt.level"

// APIHostConfig is the config key used to determine the api host
const APIHostConfig = "api.host"

// SvcAppName is the name we give our state-svc application
const SvcAppName = "State Service"

Expand Down
2 changes: 2 additions & 0 deletions internal/runbits/checkout/checkout.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/ActiveState/cli/internal/runbits/buildscript"
"github.com/ActiveState/cli/internal/runbits/git"
"github.com/ActiveState/cli/pkg/localcommit"
"github.com/ActiveState/cli/pkg/platform/api"
"github.com/ActiveState/cli/pkg/platform/api/mono/mono_models"
"github.com/ActiveState/cli/pkg/platform/authentication"
"github.com/ActiveState/cli/pkg/platform/model"
Expand Down Expand Up @@ -193,6 +194,7 @@ func CreateProjectFiles(checkoutPath, cachePath, owner, name, branch, commitID,
Language: language,
Cache: cachePath,
Portable: portable,
Host: api.HostOverride(),
})
if err != nil {
if osutils.IsAccessDeniedError(err) {
Expand Down
3 changes: 2 additions & 1 deletion internal/runbits/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/ActiveState/cli/internal/runbits/runtime/trigger"
"github.com/ActiveState/cli/pkg/buildplan"
"github.com/ActiveState/cli/pkg/localcommit"
"github.com/ActiveState/cli/pkg/platform/api"
"github.com/ActiveState/cli/pkg/platform/model"
bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner"
"github.com/ActiveState/cli/pkg/project"
Expand Down Expand Up @@ -264,7 +265,7 @@ func Update(
// Build progress URL is of the form
// https://<host>/<owner>/<project>/distributions?branch=<branch>&commitID=<commitID>
host := constants.DefaultAPIHost
if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" {
if hostOverride := api.HostOverride(); hostOverride != "" {
host = hostOverride
}
path, err := url.JoinPath(proj.Owner(), proj.Name(), constants.BuildProgressUrlPathName)
Expand Down
2 changes: 2 additions & 0 deletions internal/runners/initialize/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ActiveState/cli/internal/runbits/runtime"
"github.com/ActiveState/cli/internal/runbits/runtime/trigger"
"github.com/ActiveState/cli/pkg/localcommit"
"github.com/ActiveState/cli/pkg/platform/api"
"github.com/ActiveState/cli/pkg/platform/authentication"
"github.com/ActiveState/cli/pkg/platform/model"
bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner"
Expand Down Expand Up @@ -186,6 +187,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) {
Language: lang.String(),
Directory: path,
Private: params.Private,
Host: api.HostOverride(),
}

pjfile, err := projectfile.Create(createParams)
Expand Down
10 changes: 10 additions & 0 deletions internal/testhelpers/e2e/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Session struct {
spawned []*SpawnedCmd
ignoreLogErrors bool
cache keyCache
cfg *config.Instance
}

type keyCache map[string]string
Expand Down Expand Up @@ -203,6 +204,7 @@ func new(t *testing.T, retainDirs, updatePath bool, extraEnv ...string) *Session
if err := cfg.Set(constants.SecurityPromptConfig, false); err != nil {
require.NoError(session.T, err)
}
session.cfg = cfg

return session
}
Expand Down Expand Up @@ -790,6 +792,14 @@ func (s *Session) SetupRCFileCustom(subshell subshell.SubShell) {
require.NoError(s.T, err)
}

func (s *Session) SetConfig(key string, value interface{}) {
require.NoError(s.T, s.cfg.Set(key, value))
}

func (s *Session) GetConfig(key string) interface{} {
return s.cfg.Get(key)
}

func RunningOnCI() bool {
return condition.OnCI()
}
1 change: 1 addition & 0 deletions internal/testhelpers/tagsuite/tagsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
const (
Activate = "activate"
Analytics = "analytics"
API = "api"
Artifacts = "artifacts"
Auth = "auth"
Automation = "automation"
Expand Down
55 changes: 51 additions & 4 deletions pkg/platform/api/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import (
"os"
"strings"

configMediator "github.com/ActiveState/cli/internal/mediators/config"
"github.com/ActiveState/cli/pkg/projectfile"

"github.com/ActiveState/cli/internal/condition"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
"github.com/ActiveState/cli/internal/logging"
)

func init() {
configMediator.RegisterOption(constants.APIHostConfig, configMediator.String, "")
}

// Service records available api services
type Service string

Expand Down Expand Up @@ -49,6 +55,9 @@ const (
// ServiceHasuraInventory is the Hasura service for inventory information.
ServiceHasuraInventory = "hasura-inventory"

// ServiceUpdateInfo is the service for update info
ServiceUpdateInfo = "update-info"

// TestingPlatform is the API host used by tests so-as not to affect production.
TestingPlatform = ".testing.tld"
)
Expand Down Expand Up @@ -109,6 +118,24 @@ var urlsByService = map[Service]*url.URL{
Host: constants.DefaultAPIHost,
Path: constants.HasuraInventoryAPIPath,
},
ServiceUpdateInfo: {
Scheme: "https",
Host: constants.DefaultAPIHost,
Path: constants.UpdateInfoAPIPath,
},
}

var configuredAPIHost string

func registerConfigListener(cfg *config.Instance) {
configMediator.AddListener(constants.APIHostConfig, func() {
configuredAPIHost = cfg.GetString(constants.APIHostConfig)
})
}

func SetConfig(cfg *config.Instance) {
configuredAPIHost = cfg.GetString(constants.APIHostConfig)
registerConfigListener(cfg)
}

// GetServiceURL returns the URL for the given service
Expand All @@ -121,7 +148,7 @@ func GetServiceURL(service Service) *url.URL {
serviceURL.Host = *host
}

if insecure := os.Getenv(constants.APIInsecureEnvVarName); insecure == "true" {
if insecure := os.Getenv(constants.APIHostEnvVarName); insecure == "true" {
if serviceURL.Scheme == "https" || serviceURL.Scheme == "wss" {
serviceURL.Scheme = strings.TrimRight(serviceURL.Scheme, "s")
}
Expand All @@ -142,8 +169,9 @@ func GetServiceURL(service Service) *url.URL {
}

func getProjectHost(service Service) *string {
if apiHost := os.Getenv(constants.APIHostEnvVarName); apiHost != "" {
return &apiHost
if host := HostOverride(); host != "" {
logging.Debug("Using host override: %s", host)
return &host
}

if condition.InUnitTest() {
Expand All @@ -164,11 +192,30 @@ func getProjectHost(service Service) *string {
return &url.Host
}

func getProjectHostFromConfig() string {
if configuredAPIHost != "" {
return configuredAPIHost
}
return ""
}

func HostOverride() string {
if apiHost := os.Getenv(constants.APIHostEnvVarName); apiHost != "" {
return apiHost
}

if apiHost := getProjectHostFromConfig(); apiHost != "" {
return apiHost
}

return ""
}

// GetPlatformURL returns a generic Platform URL for the given path.
// This is for retrieving non-service URLs (e.g. signup URL).
func GetPlatformURL(path string) *url.URL {
host := constants.DefaultAPIHost
if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" {
if hostOverride := HostOverride(); hostOverride != "" {
host = hostOverride
}
return &url.URL{
Expand Down
1 change: 1 addition & 0 deletions pkg/projectfile/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func Test_Create(t *testing.T) {
Project: tt.args.project,
Directory: tt.args.directory,
Language: tt.args.language,
Host: "test.example.com",
})
assert.NoError(t, err)
configFile := filepath.Join(tempDir, constants.ConfigFileName)
Expand Down
7 changes: 4 additions & 3 deletions pkg/projectfile/projectfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ type CreateParams struct {
ProjectURL string
Cache string
Portable bool
Host string
}

// Create will create a new activestate.yaml with a projectURL for the given details
Expand All @@ -943,9 +944,9 @@ func createCustom(params *CreateParams, lang language.Language) (*Project, error

if params.ProjectURL == "" {
// Note: cannot use api.GetPlatformURL() due to import cycle.
host := constants.DefaultAPIHost
if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" {
host = hostOverride
host := params.Host
if host == "" {
host = constants.DefaultAPIHost
}
u, err := url.Parse(fmt.Sprintf("https://%s/%s/%s", host, params.Owner, params.Project))
if err != nil {
Expand Down
72 changes: 72 additions & 0 deletions test/integration/api_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,78 @@ func (suite *ApiIntegrationTestSuite) TestNoApiCallsForPlainInvocation() {
suite.Assert().True(readLogFile, "did not read log file")
}

func (suite *ApiIntegrationTestSuite) TestAPIHostConfig_SetBeforeInvocation() {
suite.OnlyRunForTags(tagsuite.API)

ts := e2e.New(suite.T(), false)
defer ts.Close()

ts.SetConfig("api.host", "test.example.com")
suite.Assert().Equal(ts.GetConfig("api.host"), "test.example.com")

cp := ts.SpawnWithOpts(
e2e.OptArgs("--version"),
)
cp.ExpectExitCode(0)
ts.IgnoreLogErrors()

correctHostCount := 0
incorrectHostCount := 0
for _, path := range ts.LogFiles() {
contents := string(fileutils.ReadFileUnsafe(path))
if strings.Contains(contents, "test.example.com") {
correctHostCount++
}
if strings.Contains(contents, "platform.activestate.com") {
incorrectHostCount++
}
}
suite.Assert().Greater(correctHostCount, 0, "Log file should contain the configured API host 'test.example.com'")
// TODO: This is failing because the state-svc is trying to update with the default host.
// This will be addressed by CP-1054 very shortly.
// suite.Assert().Equal(incorrectHostCount, 0, "Log file should not contain the default API host 'platform.activestate.com'")

// Clean up - remove the config setting
cp = ts.Spawn("config", "set", "api.host", "")
cp.Expect("Successfully")
cp.ExpectExitCode(0)
}

func (suite *ApiIntegrationTestSuite) TestAPIHostConfig_SetOnFirstInvocation() {
suite.OnlyRunForTags(tagsuite.API)

ts := e2e.New(suite.T(), false)
defer ts.Close()

cp := ts.Spawn("config", "set", "api.host", "test.example.com")
cp.Expect("Successfully")
cp.ExpectExitCode(0)

cp = ts.SpawnWithOpts(
e2e.OptArgs("--version"),
e2e.OptAppendEnv("VERBOSE=true"),
)
cp.ExpectExitCode(0)
// After setting the config, there should be no log entries for the default host.
suite.Assert().NotContains(cp.Output(), "platform.activestate.com")

// Some state-svc log entries will contain the default host as it executed requests before
// we set the config value.
correctHostCount := 0
for _, path := range ts.LogFiles() {
contents := string(fileutils.ReadFileUnsafe(path))
if strings.Contains(contents, "test.example.com") {
correctHostCount++
}
}
suite.Assert().Greater(correctHostCount, 0, "Log file should contain the configured API host 'test.example.com'")

// Clean up - remove the config setting
cp = ts.Spawn("config", "set", "api.host", "")
cp.Expect("Successfully")
cp.ExpectExitCode(0)
}

func TestApiIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(ApiIntegrationTestSuite))
}
32 changes: 32 additions & 0 deletions test/integration/config_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,38 @@ func (suite *ConfigIntegrationTestSuite) TestList() {

suite.Require().NotContains(cp.Snapshot(), constants.AsyncRuntimeConfig)
}

func (suite *ConfigIntegrationTestSuite) TestAPIHostConfig() {
suite.OnlyRunForTags(tagsuite.Config)
ts := e2e.New(suite.T(), false)
defer ts.Close()

cp := ts.Spawn("config", "set", "api.host", "test.example.com", "-o", "json")
cp.ExpectExitCode(0)
AssertValidJSON(suite.T(), cp)
cp.Expect(`{"name":"api.host","value":"test.example.com"}`)

cp = ts.Spawn("config", "get", "api.host", "-o", "json")
cp.ExpectExitCode(0)
AssertValidJSON(suite.T(), cp)
cp.Expect(`{"name":"api.host","value":"test.example.com"}`)

cp = ts.Spawn("config")
cp.Expect("api.host")
cp.Expect("test.example.com")
cp.ExpectExitCode(0)

cp = ts.Spawn("config", "set", "api.host", "", "-o", "json")
cp.ExpectExitCode(0)
AssertValidJSON(suite.T(), cp)
cp.Expect(`{"name":"api.host","value":""}`)

cp = ts.Spawn("config", "get", "api.host", "-o", "json")
cp.ExpectExitCode(0)
AssertValidJSON(suite.T(), cp)
cp.Expect(`{"name":"api.host","value":""}`)
}

func TestConfigIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(ConfigIntegrationTestSuite))
}
Loading