1
+ #!/usr/bin/env python3
2
+
3
+ # *****************************************************************************
4
+ # Copyright (c) 2025 IBM Corporation and other Contributors.
5
+ #
6
+ # All rights reserved. This program and the accompanying materials
7
+ # are made available under the terms of the Eclipse Public License v1.0
8
+ # which accompanies this distribution, and is available at
9
+ # http://www.eclipse.org/legal/epl-v10.html
10
+ #
11
+ # *****************************************************************************
12
+
13
+ from kubernetes import client , config
14
+ from kubernetes .config .config_exception import ConfigException
15
+ import argparse
16
+ import logging
17
+ import urllib3
18
+ urllib3 .disable_warnings ()
19
+ import yaml
20
+ import json
21
+ import sys
22
+
23
+ import boto3
24
+ from botocore .exceptions import ClientError
25
+
26
+ from mas .devops .users import MASUserUtils
27
+
28
+
29
+
30
+ if __name__ == "__main__" :
31
+ parser = argparse .ArgumentParser ()
32
+
33
+ # Primary Options
34
+ parser .add_argument ("--mas-instance-id" , required = True )
35
+ parser .add_argument ("--mas-workspace-id" , required = True )
36
+ parser .add_argument ("--log-level" , required = False , choices = ["DEBUG" , "INFO" , "WARNING" , "ERROR" , "CRITICAL" ], default = "INFO" )
37
+ parser .add_argument ("--coreapi-port" , required = False , default = 443 )
38
+ parser .add_argument ("--admin-dashboard-port" , required = False , default = 443 )
39
+ parser .add_argument ("--manage-api-port" , required = False , default = 443 )
40
+
41
+
42
+ group = parser .add_mutually_exclusive_group (required = True )
43
+ group .add_argument ("--initial-users-yaml-file" )
44
+ group .add_argument ("--initial-users-secret-name" )
45
+
46
+ args , unknown = parser .parse_known_args ()
47
+
48
+ log_level = getattr (logging , args .log_level )
49
+
50
+ logger = logging .getLogger ()
51
+ logger .setLevel (log_level )
52
+
53
+ ch = logging .StreamHandler ()
54
+ ch .setLevel (log_level )
55
+ chFormatter = logging .Formatter (
56
+ "%(asctime)-25s %(name)-50s [%(threadName)s] %(levelname)-8s %(message)s"
57
+ )
58
+ ch .setFormatter (chFormatter )
59
+ logger .addHandler (ch )
60
+
61
+ mas_instance_id = args .mas_instance_id
62
+ mas_workspace_id = args .mas_workspace_id
63
+ initial_users_yaml_file = args .initial_users_yaml_file
64
+ initial_users_secret_name = args .initial_users_secret_name
65
+ coreapi_port = args .coreapi_port
66
+ admin_dashboard_port = args .admin_dashboard_port
67
+ manage_api_port = args .manage_api_port
68
+
69
+
70
+ logger .info ("Configuration:" )
71
+ logger .info ("--------------" )
72
+ logger .info (f"mas_instance_id: { mas_instance_id } " )
73
+ logger .info (f"mas_workspace_id: { mas_workspace_id } " )
74
+ logger .info (f"initial_users_yaml_file: { initial_users_yaml_file } " )
75
+ logger .info (f"initial_users_secret_name: { initial_users_secret_name } " )
76
+ logger .info (f"log_level: { log_level } " )
77
+ logger .info (f"coreapi_port: { coreapi_port } " )
78
+ logger .info (f"admin_dashboard_port: { admin_dashboard_port } " )
79
+ logger .info (f"manage_api_port: { manage_api_port } " )
80
+ logger .info ("" )
81
+
82
+ try :
83
+ # Try to load in-cluster configuration
84
+ config .load_incluster_config ()
85
+ logger .debug ("Loaded in-cluster configuration" )
86
+ except ConfigException :
87
+ # If that fails, fall back to kubeconfig file
88
+ config .load_kube_config ()
89
+ logger .debug ("Loaded kubeconfig file" )
90
+
91
+
92
+ user_utils = MASUserUtils (mas_instance_id , mas_workspace_id , client .api_client .ApiClient (), coreapi_port = coreapi_port , admin_dashboard_port = admin_dashboard_port , manage_api_port = manage_api_port )
93
+
94
+ if initial_users_secret_name is not None :
95
+
96
+ logger .info (f"Loading initial_users configuration from secret { initial_users_secret_name } " )
97
+
98
+ session = boto3 .session .Session ()
99
+ aws_sm_client = session .client (
100
+ service_name = 'secretsmanager' ,
101
+ )
102
+ try :
103
+ initial_users_secret = aws_sm_client .get_secret_value ( # pragma: allowlist secret
104
+ SecretId = initial_users_secret_name
105
+ )
106
+ except ClientError as e :
107
+ if e .response ['Error' ]['Code' ] == 'ResourceNotFoundException' :
108
+ logger .info (f"Secret { initial_users_secret_name } was not found, nothing to do, exiting now." )
109
+ sys .exit (0 )
110
+
111
+ raise Exception (f"Failed to fetch secret { initial_users_secret_name } : { str (e )} " )
112
+
113
+ secret_json = json .loads (initial_users_secret ['SecretString' ])
114
+ initial_users = user_utils .parse_initial_users_from_aws_secret_json (secret_json )
115
+ elif initial_users_yaml_file is not None :
116
+ with open (initial_users_yaml_file , 'r' ) as file :
117
+ initial_users = yaml .safe_load (file )
118
+ else :
119
+ raise Exception ("Something unexpected happened" )
120
+
121
+
122
+ result = user_utils .create_initial_users_for_saas (initial_users )
123
+
124
+ # if user details were sourced from an AWS SM secret, remove the completed entries from the secret
125
+ # so we don't try and resync them the next time round (and potentially undo an update made by a customer)
126
+ if initial_users_secret_name is not None :
127
+ has_updates = False
128
+ for completed_user in result ["completed" ]:
129
+ logger .info (f"Removing synced user { completed_user ['email' ]} from { initial_users_secret_name } secret" )
130
+ secret_json .pop (completed_user ["email" ])
131
+ has_updates = True
132
+
133
+ if has_updates :
134
+ logger .info (f"Updating secret { initial_users_secret_name } " )
135
+ try :
136
+ aws_sm_client .update_secret ( # pragma: allowlist secret
137
+ SecretId = initial_users_secret_name ,
138
+ SecretString = json .dumps (secret_json )
139
+ )
140
+ except ClientError as e :
141
+ raise Exception (f"Failed to update secret { initial_users_secret_name } : { str (e )} " )
142
+
143
+
144
+ if len (result ["failed" ]) > 0 :
145
+ failed_user_ids = list (map (lambda u : u ["email" ], result ["failed" ]))
146
+ raise Exception (f"Sync failed for the following user IDs { failed_user_ids } " )
0 commit comments