Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to support customized config files to the cluster #82

Merged
merged 6 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ In order to deploy both the stacks the user needs to provide a set of required a
| enableRemoteStore | Optional | boolean | Boolean flag to enable Remote Store feature e.g., `--context enableRemoteStore=true`. See [Enable Remote Store Feature](#enable-remote-store-feature) for more details. Defaults to false |
| storageVolumeType | Optional | string | EBS volume type for all the nodes (data, ml, cluster manager). Defaults to gp2. See `lib/opensearch-config/node-config.ts` for available options. E.g., `-c storageVolumeType=gp3`. For SSD based instance (i.e. i3 family), it is used for root volume configuration. |
| customRoleArn | Optional | string | User provided IAM role arn to be used as ec2 instance profile. `-c customRoleArn=arn:aws:iam::<AWS_ACCOUNT_ID>:role/<ROLE_NAME>` |
| customConfigFiles | Optional | string | You can provide an entire config file to be overwritten or added to OpenSearch and OpenSearch Dashboards. Pass string in the form of JSON with key as local path to the config file to read from and value as file on the server to overwrite/add. Note that the values in the JSON needs to have prefix of `opensearch` or `opensearch-dashboards`. Example: `-c customConfigFiles='{"opensearch-config/config.yml": "opensearch/config/opensearch-security/config.yml", "opensearch-config/role_mapping.yml":"opensearch/config/opensearch-security/roles_mapping.yml", "/roles.yml": "opensearch/config/opensearch-security/roles.yml"}'` |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Formatting change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Thanks!


* Before starting this step, ensure that your AWS CLI is correctly configured with access credentials.
* Also ensure that you're running these commands in the current directory
Expand Down Expand Up @@ -98,7 +99,7 @@ cdk synth "*" --context securityDisabled=false \
--context distVersion=2.3.0 --context serverAccessType=ipv4 --context restrictServerAccessTo=10.10.10.10/32
```

#### Sample command to set up multi-node cluster with security disabled on x64 AL2 machine
#### Sample command to set up multi-node cluster with security enabled on x64 AL2 machine

Please note that as of now we only support instances backed by Amazon Linux-2 amis.

Expand Down
31 changes: 25 additions & 6 deletions lib/infra/infra-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,17 @@
readonly mlNodeCount: number,
readonly dataNodeStorage: number,
readonly mlNodeStorage: number,
readonly jvmSysPropsString?: string,
readonly additionalConfig?: string,
readonly additionalOsdConfig?: string,
readonly dataEc2InstanceType: InstanceType,
readonly mlEc2InstanceType: InstanceType,
readonly use50PercentHeap: boolean,
readonly isInternal: boolean,
readonly enableRemoteStore: boolean,
readonly storageVolumeType: EbsDeviceVolumeType,
readonly customRoleArn: string
readonly customRoleArn: string,
readonly jvmSysPropsString?: string,
readonly additionalConfig?: string,
readonly additionalOsdConfig?: string,
readonly customConfigFiles?: string,
}

export class InfraStack extends Stack {
Expand Down Expand Up @@ -147,7 +148,7 @@
}

if (props.singleNodeCluster) {
console.log('Single node value is true, creating single node configurations');

Check warning on line 151 in lib/infra/infra-stack.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
singleNodeInstance = new Instance(this, 'single-node-instance', {
vpc: props.vpc,
instanceType: singleNodeInstanceType,
Expand Down Expand Up @@ -585,7 +586,25 @@
}));
}

// // Startinng OpenSearch based on whether the distribution type is min or bundle
if (props.customConfigFiles !== 'undefined') {
try {
// @ts-ignore
const jsonObj = JSON.parse(props.customConfigFiles);
Object.keys(jsonObj).forEach((localFileName) => {
const getConfig = load(readFileSync(localFileName, 'utf-8'));
const remoteConfigLocation = jsonObj[localFileName];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming this provides the value, i.e, the actual path of the file in opensearch or opensearch-dashboards directory on the ec2 host?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! root directory remains the same which is /home/ec2-user, the value of json needs to be opensearch/config/* etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, for local file name, the user has to provide the absolute path or does the code reads using relative-path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both works as per my testing. Relative as well as absolute path.

cfnInitConfig.push(InitCommand.shellCommand(`set -ex; echo "${dump(getConfig)}" > ${remoteConfigLocation}`,
{
cwd: '/home/ec2-user',
ignoreErrors: false,
}));
});
} catch (e) {
throw new Error(`Encountered following error while parsing customConfigFiles json parameter: ${e}`);
}
}

// Starting OpenSearch based on whether the distribution type is min or bundle
if (props.minDistribution) { // using (stackProps.minDistribution) condition is not working when false value is being sent
cfnInitConfig.push(InitCommand.shellCommand('set -ex;cd opensearch; sudo -u ec2-user nohup ./bin/opensearch >> install.log 2>&1 &',
{
Expand Down Expand Up @@ -635,7 +654,7 @@
}));
}

// Startinng OpenSearch-Dashboards
// Starting OpenSearch-Dashboards
cfnInitConfig.push(InitCommand.shellCommand('set -ex;cd opensearch-dashboards;'
+ 'sudo -u ec2-user nohup ./bin/opensearch-dashboards > dashboard_install.log 2>&1 &', {
cwd: '/home/ec2-user',
Expand Down
10 changes: 7 additions & 3 deletions lib/os-cluster-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class OsClusterEntrypoint {
let dataEc2InstanceType: InstanceType;
let mlEc2InstanceType: InstanceType;
let volumeType: EbsDeviceVolumeType;
let customConfigFiles: string = 'undefined';

const x64InstanceTypes: string[] = Object.keys(x64Ec2InstanceType);
const arm64InstanceTypes: string[] = Object.keys(arm64Ec2InstanceType);
Expand Down Expand Up @@ -202,6 +203,8 @@ export class OsClusterEntrypoint {
}
}

customConfigFiles = `${scope.node.tryGetContext('customConfigFiles')}`;

const suffix = `${scope.node.tryGetContext('suffix')}`;
const networkStackSuffix = `${scope.node.tryGetContext('networkStackSuffix')}`;

Expand Down Expand Up @@ -265,14 +268,15 @@ export class OsClusterEntrypoint {
singleNodeCluster: isSingleNode,
dataNodeStorage,
mlNodeStorage,
jvmSysPropsString: jvmSysProps,
additionalConfig: ymlConfig,
additionalOsdConfig: osdYmlConfig,
use50PercentHeap,
isInternal,
enableRemoteStore,
storageVolumeType: volumeType,
customRoleArn,
jvmSysPropsString: jvmSysProps,
additionalConfig: ymlConfig,
additionalOsdConfig: osdYmlConfig,
customConfigFiles,
...props,
});

Expand Down
23 changes: 23 additions & 0 deletions test/data/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
_meta:
type: "allowlist"
config_version: 2

# Description:
# enabled - feature flag.
# if enabled is false, all endpoints are accessible.
# if enabled is true, all users except the SuperAdmin can only submit the allowed requests to the specified endpoints.
# SuperAdmin can access all APIs.
# SuperAdmin is defined by the SuperAdmin certificate, which is configured with the opensearch.yml setting plugins.security.authcz.admin_dn:
# Refer to the example setting in opensearch.yml to learn more about configuring SuperAdmin.
#
# requests - map of allow listed endpoints and HTTP requests

#this name must be config
config:
enabled: true
requests:
/_cluster/settings:
- GET
/_cat/nodes:
- GET
27 changes: 27 additions & 0 deletions test/data/roles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
complex-role:
reserved: false
hidden: false
cluster_permissions:
- "read"
- "cluster:monitor/nodes/stats"
- "cluster:monitor/task/get"
index_permissions:
- index_patterns:
- "opensearch_dashboards_sample_data_*"
dls: "{\"match\": {\"FlightDelay\": true}}"
fls:
- "~FlightNum"
masked_fields:
- "Carrier"
allowed_actions:
- "read"
tenant_permissions:
- tenant_patterns:
- "analyst_*"
allowed_actions:
- "kibana_all_write"
static: false
_meta:
type: "roles"
config_version: 2
35 changes: 35 additions & 0 deletions test/os-cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ test('Test Resources with security disabled multi-node default instance types',
restrictServerAccessTo: 'all',
additionalConfig: '{ "name": "John Doe", "age": 30, "email": "[email protected]" }',
additionalOsdConfig: '{ "something.enabled": "true", "something_else.enabled": "false" }',
// eslint-disable-next-line max-len
customConfigFiles: '{"test/data/config.yml": "opensearch/config/opensearch-security/config.yml", "test/data/roles.yml": "opensearch/config/opensearch-security/roles.yml"}',
},
});

Expand Down Expand Up @@ -489,3 +491,36 @@ test('Test multi-node cluster with custom IAM Role', () => {
Roles: ['customRoleName'],
});
});

test('Throw error on incorrect JSON', () => {
const app = new App({
context: {
securityDisabled: true,
minDistribution: false,
distributionUrl: 'www.example.com',
cpuArch: 'x64',
singleNodeCluster: false,
dashboardsUrl: 'www.example.com',
distVersion: '1.0.0',
serverAccessType: 'ipv4',
restrictServerAccessTo: 'all',
additionalConfig: '{ "name": "John Doe", "age": 30, "email": "[email protected]" }',
additionalOsdConfig: '{ "something.enabled": "true", "something_else.enabled": "false" }',
// eslint-disable-next-line max-len
customConfigFiles: '{"test/data/config.yml": opensearch/config/opensearch-security/config.yml"}',
},
});
// WHEN
try {
const testStack = new OsClusterEntrypoint(app, {
env: { account: 'test-account', region: 'us-east-1' },
});

// eslint-disable-next-line no-undef
fail('Expected an error to be thrown');
} catch (error) {
expect(error).toBeInstanceOf(Error);
// eslint-disable-next-line max-len
expect(error.message).toEqual('Encountered following error while parsing customConfigFiles json parameter: SyntaxError: Unexpected token o in JSON at position 25');
}
});
Loading