refactor env parameters#2394
Conversation
Hello sylvainsenechal,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
b80c535 to
0ddc9e7
Compare
c016cb6 to
c34e56a
Compare
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
|
c34e56a to
7dfe4dc
Compare
0ddc9e7 to
6605bd7
Compare
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
|
6605bd7 to
2264a31
Compare
1805181 to
7b2e0f5
Compare
4c2c959 to
a8934b3
Compare
a8934b3 to
54cc19b
Compare
59d7858 to
a856b60
Compare
f13d91c to
1c39486
Compare
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
|
1c39486 to
eb86c97
Compare
eb86c97 to
0ae18c9
Compare
| if (Object.keys(config).length > 0) return; | ||
| const zenkoAccountCredentials = await loadZenkoAccount(); | ||
| const clients = createClients(zenkoAccountCredentials); | ||
| Object.assign(config, { |
There was a problem hiding this comment.
initConfig() is all-or-nothing: if any single K8s resource lookup fails (e.g., loadKafkaTopics when the cold-sorbet-config secret doesn't exist), the Object.assign never executes and config stays empty — losing even the resources that did load successfully (like admin/account credentials).
Since mocha tests now also call initConfig() via the root hook, any mocha test suite running in an environment without the full Zenko stack (no sorbet, no Zenko CR) will fail at startup, even though those tests only need account/admin credentials.
Consider wrapping each loader independently so partial failures don't prevent the rest of the config from being populated, or splitting the init into required-vs-optional groups.
There was a problem hiding this comment.
yep and im fine with it.
wrapping each loader would means that every time someone wants to use a field in this config, they first have to wonder "is this gonna be undefined because im in this particular test or not", and have some weird failures later down the line because some variable is undefined
I don't want that. Good long term goal is one zenko setup for all tests
basically fail fast and be explicit
| import { NodeHttpHandler } from '@smithy/node-http-handler'; | ||
| import { getSecretByLabel, getSecretByName, getCustomObject } from "./kubernetes"; | ||
|
|
||
| const ZENKO_NAME = 'end2end'; |
There was a problem hiding this comment.
ZENKO_NAME is hardcoded to 'end2end', but the shell script (setup-e2e-env.sh) accepts it as a positional argument (${1:-end2end}). This removes the ability to run tests against a differently-named Zenko installation. Consider reading from process.env.ZENKO_NAME with a default.
| const ZENKO_NAME = 'end2end'; | |
| const ZENKO_NAME = process.env.ZENKO_NAME || 'end2end'; |
There was a problem hiding this comment.
kinda good catch but actually what im gonna change here is the positional argument in setup e2e, which is never used, so im removing it
There was a problem hiding this comment.
we should keep it.
goal (eventually) is to be able to run against any zenko instance (e.g. artesca), so best to keep it: it does not hurt.
There was a problem hiding this comment.
Ok I kept it but current state is not super nice : grep 'end2end' returns 164 results so if we want to use that variable zenko name there are many places that will need to change
0ae18c9 to
b59bdf4
Compare
There was a problem hiding this comment.
Review the /tests_common folder first, at the end of this pr
|
|
||
| . "$_SETUP_DIR/common.sh" | ||
|
|
||
| ZENKO_NAME="${ZENKO_NAME:-end2end}" |
There was a problem hiding this comment.
argument never provided so removing ambiguity
| export STORAGE_ACCOUNT_OWNER="${STORAGE_ACCOUNT_OWNER_USER_NAME}" | ||
| export DATA_CONSUMER="${DATA_CONSUMER_USER_NAME}" | ||
| export DATA_ACCESSOR="${DATA_ACCESSOR_USER_NAME}" | ||
| export ACCOUNT="zenko-ctst" |
There was a problem hiding this comment.
some stuffs like this needs to stay here, they are used by cli-testing, or are coupled with other script/logic and can't be migrated easily without some other preliminary work
| } | ||
| Identity.useIdentity(IdentityEnum.ACCOUNT, world.getSaved<string>('accountName') || | ||
| world.parameters.AccountName); | ||
| Identity.useIdentity(IdentityEnum.ACCOUNT, world.getSaved<string>('accountName') || ZENKO_ACCOUNT_NAME); |
There was a problem hiding this comment.
you'll see a lot of similar changes like this in the pr :
I'm removing ambiguity as much as possible when it is not used and not useful
There was a problem hiding this comment.
instead of updating code all over the place, should we not initialize the world (why drop support for world.parameters) once on startup?
There was a problem hiding this comment.
The current world parameters is currently automatically loaded from the --world-parameters json value.
The new type I added loads value with a kubernete client
Do you mean that you would prefer all the parameters staying in the ZenkoWorldParameters ? I think it's possible but may introduce some other complexities (the world is recreated before each scenario in a Before hook, with Zenko.init), + I want the new TestParameters to be usable with any tests suite, and I don't like the --world-parameters so much (only strings allowed, need to define values in bash script) so i want to avoid ultimately have everything in the new TestParameters interface
There was a problem hiding this comment.
I mean parameters should be a World -i.e. Zenko-, as it is the standard way to define the environment for cucumber tests.
We may keep parameters in ZenkoWorldParameters, or possibly remove/tweak some; and use the new "config" lib to initialize the Zenko world instance. But IMHO there should not be coupling between tests and the config lib: should be handled through the world.
| const client = new ScubaClient({ | ||
| port: parseInt(this.parameters.UtilizationServicePort), | ||
| host: this.parameters.UtilizationServiceHost, | ||
| port: 80, |
There was a problem hiding this comment.
could be stored in some UTILIZATION_SERVICE_PORT = 80 const, I did it for a few other variables.
Not super important, its just always 80 defautl http port
| accessKeyId: this.parameters.AccountAccessKey, | ||
| secretAccessKey: this.parameters.AccountSecretKey, | ||
| }); | ||
| Identity.defaultAccountName = ZENKO_ACCOUNT_NAME; |
There was a problem hiding this comment.
Side note on this whole long Identity thing part :
I'm thinking this is gonna change again in another pr, I don't find it super clear, lot's of ifs(somethingDefined), very smelly. It works but not very clean
| break; | ||
| case EntityType.STORAGE_MANAGER: | ||
| await this.prepareARWWI(this.parameters.StorageManagerUsername || 'storage_manager', | ||
| await this.prepareARWWI('storage_manager', |
There was a problem hiding this comment.
I will probably redefine constants later, but same thing : this is also defined in cli-testing, there is some annoying coupling, don't wanna touch it too much if its gonna change again later
| }); | ||
|
|
||
| describe('Backbeat replication metrics data', function dF() { | ||
| let scalityUtils; |
There was a problem hiding this comment.
Lots of upcoming pattern like this one, moving top level definition into before hooks :
Can't define these at the top level because the config is not loaded yet at that time (clients are created a in mocha before all setup)
There was a problem hiding this comment.
not sure it's better, but could be done if the "config" exported by 'tests_common/configuration' is the result of initConfig (same pattern as cloudserver's Config)
There was a problem hiding this comment.
I'm not sure which pattern you are referencing from cloudserver
There was a problem hiding this comment.
in cloudserver, config.js it looks like this:
class Config {
constructor() {
// load config file....
}
};
export {
config: new Config(),
Config,
};
so when you import config from cloudserver you immediately get the actual config which was loaded
There was a problem hiding this comment.
this whole file should disappear, and we should just initialize clients from the widely available config.
then tests could simply call config.vaultClient, config.s3Client etc
There was a problem hiding this comment.
this is picked up & run automatically by mocha on startup
| 'ascii', | ||
| ); | ||
| } | ||
| if (VAULT_ENDPOINT.startsWith('https://')) { |
There was a problem hiding this comment.
vaultendpoint is hardcoded as http so this is never run 👀
Keeping it for now because I'm thinking at some there was probably a good reason to have these, maybe will remove later, not really the role of this pr
| }; | ||
| PRAAdmin?: ServiceUserCredentials; | ||
| AdminCredentials: ServiceUserCredentials; | ||
| ZenkoAccount: { |
There was a problem hiding this comment.
Might deserve some renaming later or re organisation later, or structure/substrucutre, but it will come more naturally when more variables are migrated here
There was a problem hiding this comment.
even if it ends up being changed later, "ZenkoAccount" struct is really weird : an s3Client and IamClient are not part of an account ; and credentials is the actual identifiers of the account...
→ better to just remove the nesting?
francoisferrand
left a comment
There was a problem hiding this comment.
Great improvement to move from setup script to TS module;
However lots of the changes seems to be here just for the sake of it: the config object is really just being used as world was, so it would all more naturally fit to split responsibility:
- tests_common handles reading all parameters from the environment, (eventually) helpers to setup tunnels and clients...
- worldParameters is THE standard way to pass parameters in cucumber.js (and already used), so it should stay : the change should just update these world parameters using tests_common
This would greatly reduce the scope of the change, while separating responsibility (reading env VS adapting config for cucumber).
| # --- 13. Kafka host from backbeat config + port-forward --- | ||
| KAFKA_HOST_PORT_ORIG=$(kubectl get secret -l app.kubernetes.io/name=backbeat-config,app.kubernetes.io/instance=${ZENKO_NAME} \ | ||
| -o jsonpath='{.items[0].data.config\.json}' | base64 -di | jq -r .kafka.hosts) | ||
| KAFKA_SVC=${KAFKA_HOST_PORT_ORIG%:*} | ||
| KAFKA_PORT=${KAFKA_HOST_PORT_ORIG#*:} | ||
|
|
||
| # Port-forward for Kafka broker (TCP — no ingress possible) | ||
| if ! ss -tlnp 2>/dev/null | grep -q ":${KAFKA_PORT}" && \ | ||
| ! lsof -i ":${KAFKA_PORT}" &>/dev/null; then | ||
| kubectl port-forward "svc/${KAFKA_SVC}" "${KAFKA_PORT}:${KAFKA_PORT}" &>/dev/null & | ||
| _KAFKA_PF_PID=$! | ||
| timeout 10 bash -c "until ss -tlnp 2>/dev/null | grep -q ':${KAFKA_PORT}'; do sleep 0.2; done" | ||
| fi | ||
| export KAFKA_HOST_PORT="localhost:${KAFKA_PORT}" |
There was a problem hiding this comment.
not for this PR, but kafka port forward can be setup from tests as well.
(also, FYI: in Golang you can (in the kafka client) handle the "resolve" part, so you don't need to modify /etc/hosts ; it would be great if we can do the same in node...)
There was a problem hiding this comment.
Ok I didn't know we could do that from the tests directly, we can do it later yeah
There was a problem hiding this comment.
please create followup ticket
| @@ -235,20 +197,6 @@ | |||
| fi | |||
| export PROMETHEUS_SERVICE="${PROMETHEUS_SVC}.${NAMESPACE}.svc.cluster.local" | |||
There was a problem hiding this comment.
could be dynamically retrieved as well?
there is most likely a single prometheus service in the cluster: we could find it by labels ("managed by prometheus operator") or checking the prometheus CR...
There was a problem hiding this comment.
There are multiple values we can load that I didn't add yet
My problem is, for PROMETHEUS_SERVICE for example, its built from PROMETHEUS_SVC, but PROMETHEUS_SVC is used right before that to do the port forward. So If I move PROMETHEUS_SERVICE to the ts code, I still need to have it defined in the sh script and then we have the same variable defined in 2 places. Best is to keep it here for now, as you said later we should be able to do the port forward from the node code so we can do it here
There was a problem hiding this comment.
please create followup ticket
| export AZURE_ARCHIVE_ACCESS_TIER="Hot" | ||
| export AZURE_ARCHIVE_MANIFEST_ACCESS_TIER="Hot" |
There was a problem hiding this comment.
nothing specific here (I think), should be in tests or read from cluster...
There was a problem hiding this comment.
Yeah i can move it now since you mention it, but the reason I didnt do it is just that I didnt fully review the whole file as the pr is getting big
| } | ||
| Identity.useIdentity(IdentityEnum.ACCOUNT, world.getSaved<string>('accountName') || | ||
| world.parameters.AccountName); | ||
| Identity.useIdentity(IdentityEnum.ACCOUNT, world.getSaved<string>('accountName') || ZENKO_ACCOUNT_NAME); |
There was a problem hiding this comment.
instead of updating code all over the place, should we not initialize the world (why drop support for world.parameters) once on startup?
| BeforeAll(async function () { | ||
| // Some hooks are defined in cli-testing and use the configuration, | ||
| // we need to have this run before anything else | ||
| await initConfig(); |
There was a problem hiding this comment.
since we need to init config anyway : best initialize the world, which is the CTST (or even cucumber?) concept to hold such configuration
There was a problem hiding this comment.
I explored a bit the best strategy to deal with this config initialization, but I think here is the best place to do it.
The World class is created for each scenario, the constructor of the world can't be asynchronous, so its a bit tricky to do this initialization in the world constructor or as a static config.
BeforeAll is run once per worker, before the world creation, and before pretty much everything since its the first beforeAll, so the config is loaded once and available everywhere
There was a problem hiding this comment.
so World constructor will be called after config is init, and can used the config directly?
| }); | ||
|
|
||
| describe('Backbeat replication metrics data', function dF() { | ||
| let scalityUtils; |
There was a problem hiding this comment.
not sure it's better, but could be done if the "config" exported by 'tests_common/configuration' is the result of initConfig (same pattern as cloudserver's Config)
| import { NodeHttpHandler } from '@smithy/node-http-handler'; | ||
| import { getSecretByLabel, getSecretByName, getCustomObject } from "./kubernetes"; | ||
|
|
||
| const ZENKO_NAME = 'end2end'; |
There was a problem hiding this comment.
we should keep it.
goal (eventually) is to be able to run against any zenko instance (e.g. artesca), so best to keep it: it does not hurt.
b59bdf4 to
b5c9722
Compare
francoisferrand
left a comment
There was a problem hiding this comment.
as discussed, pending some rework before reviewing
Issue: ZENKO-5259
Mostly refactoring around tests configuration.