Skip to content

Commit

Permalink
feat: add OpenSearch #21 (#31)
Browse files Browse the repository at this point in the history
* feat: add OpenSearch

* feat: unify ElasticSearch and OpenSearch configuration

Changes:
- set the same cluster and service name
- unify tutor plugin configuration variables
  • Loading branch information
cmltaWt0 authored Jul 12, 2023
1 parent 7f68330 commit 1289b3a
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ To enable set `elasticsearch.enabled=true` in your `values.yaml` and deploy the
For each instance you would like to enable this on, set the configuration values in the respective `config.yml`:

```yaml
K8S_HARMONY_ENABLE_SHARED_ELASTICSEARCH: true
K8S_HARMONY_ENABLE_SHARED_HARMONY_SEARCH: true
RUN_ELASTICSEARCH: false
```
Expand Down
7 changes: 5 additions & 2 deletions charts/harmony-chart/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ dependencies:
- name: vertical-pod-autoscaler
repository: https://cowboysysop.github.io/charts/
version: 6.0.3
digest: sha256:4915c21724a8c4693f749aab2c311336990b48e16e90884d7b9ceca42eef69f8
generated: "2023-04-13T23:07:43.616994532-05:00"
- name: opensearch
repository: https://opensearch-project.github.io/helm-charts
version: 2.13.3
digest: sha256:11b69b1ea771337b1e7cf8497ee342a25b095b86899b8cee716be8cc9f955559
generated: "2023-07-01T19:23:29.18815+03:00"
7 changes: 6 additions & 1 deletion charts/harmony-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ version: 0.1.0
# version the application is using. It is recommended to use it with quotes.
#
# In our case, this represents the version of Tutor that this chart is compatible with.
appVersion: "14.1.1"
appVersion: "16.0.2"

dependencies:
# This is just info for the "helm dependency update" command, which will update the ./charts/ directory when run, using
Expand Down Expand Up @@ -42,3 +42,8 @@ dependencies:
repository: https://cowboysysop.github.io/charts/
alias: vpa
condition: vpa.enabled

- name: opensearch
version: "2.13.3"
condition: opensearch.enabled
repository: https://opensearch-project.github.io/helm-charts
3 changes: 2 additions & 1 deletion charts/harmony-chart/templates/elasticsearch/secrets.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
{{- $ca := genCA "elasticca" 1825 }}
{{- $cert := genSignedCert "elasticsearch-master.{{ Release.Namespace }}.local" nil (list "elasticsearch-master.{{ Release.Namespace }}.local") 1825 $ca }}
{{- $cn := printf "elasticsearch-master.%s.local" .Release.Namespace }}
{{- $cert := genSignedCert $cn nil (list $cn) 1825 $ca }}
apiVersion: v1
kind: Secret
metadata:
Expand Down
24 changes: 24 additions & 0 deletions charts/harmony-chart/templates/opensearch/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
{{- $ca := genCA "opensearchca" 1825 }}
{{- $cn := printf "opensearch-master.%s.local" .Release.Namespace }}
{{- $cert := genSignedCert $cn nil (list $cn) 1825 $ca }}
apiVersion: v1
kind: Secret
metadata:
name: opensearch-certificates
type: Opaque
data:
"ca.crt": {{ $ca.Cert | b64enc | toYaml | indent 4}}
"tls.key": {{ $cert.Key | b64enc | toYaml | indent 4}}
"tls.crt": {{ print $cert.Cert $ca.Cert | b64enc | toYaml | indent 4}}
---
{{- $password := randAlphaNum 32 }}
{{- $password_bcrypt := $password | bcrypt }}
apiVersion: v1
kind: Secret
metadata:
name: opensearch-credentials
type: Opaque
data:
harmony_password: {{ $password | b64enc | quote }}
internal_users.yml: {{ printf "---\n_meta:\n type: \"internalusers\"\n config_version: 2\n\nharmony:\n hash: \"%s\"\n reserved: true\n backend_roles:\n - \"admin\"\n description: \"Harmony admin user\"\n" $password_bcrypt | b64enc | quote }}
114 changes: 110 additions & 4 deletions charts/harmony-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ cert-manager:
elasticsearch:
enabled: false

clusterName: "harmony-search-cluster"
masterService: "harmony-search-cluster"

# Operators will need to add/update the following setting in each
# of their instances by running the commands:
# ```
# tutor config save --set K8S_HARMONY_ENABLE_SHARED_ELASTICSEARCH=true --set RUN_ELASTICSEARCH=false
# tutor config save --set K8S_HARMONY_ENABLE_SHARED_HARMONY_SEARCH=true --set RUN_ELASTICSEARCH=false
# tutor harmony create-elasticsearch-user
# ```
# RUN_ELASTICSEARCH: false
# ELASTICSEARCH_PREFIX_INDEX: "username-"
# K8S_HARMONY_USE_SHARED_ELASTICSEARCH: true
# ELASTICSEARCH_AUTH: "username:actual_password"
# HARMONY_SEARCH_INDEX_PREFIX: "username-"
# K8S_HARMONY_ENABLE_SHARED_HARMONY_SEARCH: true
# HARMONY_SEARCH_HTTP_AUTH: "username:actual_password"

# We will create the relevant certs, because they need to shared
# with pods in other namespaces.
Expand All @@ -52,6 +55,7 @@ elasticsearch:

esConfig:
"elasticsearch.yml": |
cluster.name: harmony-search-cluster
xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: /usr/share/elasticsearch/config/certs/tls.key
Expand All @@ -77,3 +81,105 @@ vpa:
# for all available options
admissionController:
replicaCount: 1


# Multi-tenant OpenSearch
opensearch:
enabled: false
clusterName: "harmony-search-cluster"
masterService: "harmony-search-cluster"

# Operators will need to add/update the following setting in each
# of their instances by running the commands:
# ```
# tutor config save --set K8S_HARMONY_ENABLE_SHARED_HARMONY_SEARCH=true --set RUN_ELASTICSEARCH=false
# tutor harmony create-opensearch-user
# ```
# RUN_ELASTICSEARCH: false
# HARMONY_SEARCH_INDEX_PREFIX: "username-"
# K8S_HARMONY_USE_SHARED_OPENSEARCH: true
# HARMONY_SEARCH_HTTP_AUTH: "username:actual_password"


# # This secret will contain the ssl certificates.
secretMounts:
- name: opensearch-certificates
secretName: opensearch-certificates
path: /usr/share/opensearch/config/certs
defaultMode: 0777

resources:
requests:
cpu: "1000m"
memory: "100Mi"

persistence:
size: 30Gi

# Set vm.max_map_count
# Default value is 262144
sysctlInit:
enabled: true

extraEnvs:
- name: DISABLE_INSTALL_DEMO_CONFIG
value: "true"
- name: HARMONY_PASSWORD
valueFrom:
secretKeyRef:
name: opensearch-credentials
key: harmony_password

# Allows you to add any config files in {{ .Values.opensearchHome }}/config
opensearchHome: /usr/share/opensearch
# such as opensearch.yml and log4j2.properties

securityConfig:
enabled: true
internalUsersSecret: opensearch-credentials

config:
opensearch.yml: |
cluster.name: harmony-search-cluster
network.host: 0.0.0.0
plugins:
security:
ssl:
transport:
enabled: true
pemcert_filepath: certs/tls.crt
pemkey_filepath: certs/tls.key
pemtrustedcas_filepath: certs/ca.crt
enforce_hostname_verification: false
http:
enabled: true
pemcert_filepath: certs/tls.crt
pemkey_filepath: certs/tls.key
pemtrustedcas_filepath: certs/ca.crt
authcz:
admin_dn:
- CN=opensearch-master.*.local
nodes_dn:
- CN=opensearch-master.*.local
allow_unsafe_democertificates: false
allow_default_init_securityindex: true
audit.type: internal_opensearch
enable_snapshot_restore_privilege: true
check_snapshot_restore_write_privileges: true
restapi:
roles_enabled: ["all_access", "security_rest_api_access"]
system_indices:
enabled: true
indices:
[
".opendistro-alerting-config",
".opendistro-alerting-alert*",
".opendistro-anomaly-results*",
".opendistro-anomaly-detector*",
".opendistro-anomaly-checkpoints",
".opendistro-anomaly-detection-state",
".opendistro-reports-*",
".opendistro-notifications-*",
".opendistro-notebooks",
".opendistro-asynchronous-search-response*",
]
Binary file added harmony-chart/charts/opensearch-2.11.4.tgz
Binary file not shown.
46 changes: 43 additions & 3 deletions tutor-contrib-harmony-plugin/tutor_k8s_harmony_plugin/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from tutor import config as tutor_config
from tutor import env as tutor_env
from tutor.commands.k8s import K8sContext, kubectl_exec
from .elasticsearch import ElasticSearchAPI
from .harmony_search.elasticsearch import ElasticSearchAPI
from .harmony_search.opensearch import OpenSearchAPI

@click.group(help="Commands and subcommands of the openedx-k8s-harmony.")
@click.pass_context
Expand All @@ -21,10 +22,10 @@ def create_elasticsearch_user(context: click.Context):
config = tutor_config.load(context.root)
namespace = config["K8S_HARMONY_NAMESPACE"]
api = ElasticSearchAPI(namespace)
username, password = config["ELASTICSEARCH_HTTP_AUTH"].split(":", 1)
username, password = config["HARMONY_SEARCH_HTTP_AUTH"].split(":", 1)
role_name = f"{username}_role"

prefix = config["ELASTICSEARCH_INDEX_PREFIX"]
prefix = config["HARMONY_SEARCH_INDEX_PREFIX"]
api.post(
f"_security/role/{role_name}",
{"indices": [{"names": [f"{prefix}*"], "privileges": ["all"]}]},
Expand All @@ -40,5 +41,44 @@ def create_elasticsearch_user(context: click.Context):
},
)

@click.command(help="Create or update Opensearch users")
@click.pass_obj
def create_opensearch_user(context: click.Context):
"""
Creates or updates the Opensearch user
"""
config = tutor_config.load(context.root)
namespace = config["K8S_HARMONY_NAMESPACE"]
api = OpenSearchAPI(namespace)
username, password = config["HARMONY_SEARCH_HTTP_AUTH"].split(":", 1)
role_name = f"{username}_role"

prefix = config["HARMONY_SEARCH_INDEX_PREFIX"]
api.put(
f"_plugins/_security/api/roles/{role_name}",
{"index_permissions": [{
"index_patterns": [
f"{prefix}*"
],
"allowed_actions": [
"read",
"write",
"create_index",
"manage",
"manage_ilm",
"all"
]
}]},
)

api.put(
f"_plugins/_security/api/internalusers/{username}",
{
"password": password,
"opendistro_security_roles": [role_name],
},
)


harmony.add_command(create_elasticsearch_user)
harmony.add_command(create_opensearch_user)
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from tutor import utils


class ElasticSearchAPI:
class BaseSearchAPI:
"""
Helper class to interact with the ElasticSearch
Helper class to interact with the HarmonySearch
API on the deployed cluster.
"""

Expand All @@ -18,18 +18,19 @@ def __init__(self, namespace):
"--tty",
"--namespace",
namespace,
"elasticsearch-master-0",
"harmony-search-cluster-master-0",
"--",
"bash",
"-c",
]
self._curl_base = ["curl", "--insecure", "-u", "elastic:${ELASTIC_PASSWORD}"]
# Must be specified by subclasses
self._curl_base = None

def run_command(self, curl_options) -> typing.Union[dict, bytes]:
"""
Invokes a curl command on the first Elasticsearch pod.
Invokes a curl command on the first HarmonySearch pod.
If possible returns the parsed json from the Elasticsearch response.
If possible returns the parsed json from the HarmonySearch response.
Otherwise, the raw bytes from the curl command are returned.
"""
response = utils.check_output(
Expand All @@ -42,20 +43,20 @@ def run_command(self, curl_options) -> typing.Union[dict, bytes]:

def get(self, endpoint):
"""
Runs a GET request on the Elasticsearch cluster with the specified
Runs a GET request on the HarmonySearch cluster with the specified
endpoint.
If possible returns the parsed json from the Elasticsearch response.
If possible returns the parsed json from the HarmonySearch response.
Otherwise, the raw bytes from the curl command are returned.
"""
return self.run_command(["-XGET", f"https://localhost:9200/{endpoint}"])

def post(self, endpoint: str, data: dict) -> typing.Union[dict, bytes]:
"""
Runs a POST request on the Elasticsearch cluster with the specified
Runs a POST request on the HarmonySearch cluster with the specified
endpoint.
If possible returns the parsed json from the Elasticsearch response.
If possible returns the parsed json from the HarmonySearch response.
Otherwise, the raw bytes from the curl command are returned.
"""
return self.run_command(
Expand All @@ -68,3 +69,22 @@ def post(self, endpoint: str, data: dict) -> typing.Union[dict, bytes]:
'"Content-Type: application/json"',
]
)

def put(self, endpoint: str, data: dict) -> typing.Union[dict, bytes]:
"""
Runs a PUT request on the HarmonySearch cluster with the specified
endpoint.
If possible returns the parsed json from the HarmonySearch response.
Otherwise, the raw bytes from the curl command are returned.
"""
return self.run_command(
[
"-XPUT",
f"https://localhost:9200/{endpoint}",
"-d",
f"'{json.dumps(data)}'",
"-H",
'"Content-Type: application/json"',
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .base import BaseSearchAPI


class ElasticSearchAPI(BaseSearchAPI):
"""
Helper class to interact with the ElasticSearch
API on the deployed cluster.
"""

def __init__(self, namespace):
super().__init__(namespace)
self._curl_base = ["curl", "--insecure", "-u", "elastic:${ELASTIC_PASSWORD}"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .base import BaseSearchAPI


class OpenSearchAPI(BaseSearchAPI):
"""
Helper class to interact with the OpenSearch
API on the deployed cluster.
"""

def __init__(self, namespace):
super().__init__(namespace)
# TODO: Make this configurable
self._curl_base = ["curl", "--insecure", "-u", "harmony:${HARMONY_PASSWORD}"]
Loading

0 comments on commit 1289b3a

Please sign in to comment.