Skip to content

Commit 9d5de64

Browse files
authored
Add target configuration credential output method (#902)
* make sure json secrets method also injects values * remove duplicate env variable * refactor and introduce new field in target.json for secrets output method * refactor and cleanup dictionary before writing to json file * prevent model named the same as output file * remove <user> for weblogic credentials * do not complain existing secrets * write new variable file to output dir if one is provided for target operation * append the properties to the end of original properties file * prevent complaints for @@env if target * various fixes for handling variable files and output dir, added handling for existing secrets but comment out * use target configuration validation method to check for whether reporting error * change the output file to use existing variable file name and merge/replace existing properties * empty username in secret if it is started with @@secret: * fixing broken method used to generate script_hash * change file names * fix model file names comparison with additional_output logic
1 parent 7534698 commit 9d5de64

File tree

14 files changed

+187
-34
lines changed

14 files changed

+187
-34
lines changed

core/src/main/python/discover.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,13 @@ def __check_and_customize_model(model, model_context, aliases, credential_inject
445445
credential_cache = credential_injector.get_variable_cache()
446446

447447
# Generate k8s create secret script
448-
if target_configuration.uses_credential_secrets():
448+
if target_configuration.generate_script_for_secrets():
449449
target_configuration_helper.generate_k8s_script(model_context, credential_cache, model.get_model(),
450450
ExceptionType.DISCOVER)
451451

452+
if target_configuration.generate_json_for_secrets():
453+
target_configuration_helper.generate_k8s_json(model_context, credential_cache, model.get_model())
454+
452455
# create additional output after filtering, but before variables have been inserted
453456
if model_context.is_targetted_config():
454457
target_configuration_helper.create_additional_output(model, model_context, aliases, credential_injector,

core/src/main/python/prepare_model.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,28 @@ def walk(self):
241241

242242
# create a merged model that is not substituted
243243
merged_model_dictionary = {}
244-
245244
try:
246245
model_file_list = self.model_files.split(',')
246+
target = self.model_context.get_target()
247+
247248
for model_file in model_file_list:
248249
if os.path.splitext(model_file)[1].lower() == ".yaml":
249250
model_file_name = model_file
250-
FileToPython(model_file_name, True).parse()
251+
252+
if target is not None and self.model_context.get_target_configuration().get_additional_output_types():
253+
additional_output_types = []
254+
output_types = self.model_context.get_target_configuration().get_additional_output_types()
255+
if isinstance(output_types, list):
256+
additional_output_types.extend(output_types)
257+
else:
258+
additional_output_types.append(output_types)
259+
260+
if os.path.basename(model_file_name) in additional_output_types:
261+
self._logger.severe('WLSDPLY-05802', os.path.basename(model_file_name),
262+
additional_output_types, target)
263+
return VALIDATION_FAIL
264+
265+
FileToPython(model_file_name, True).parse()
251266

252267
aliases = Aliases(model_context=self.model_context, wlst_mode=WlstModes.OFFLINE)
253268

@@ -257,7 +272,7 @@ def walk(self):
257272
model_dictionary = cla_helper.merge_model_files(model_file_name, None)
258273

259274
variable_file = self.model_context.get_variable_file()
260-
if not os.path.exists(variable_file):
275+
if variable_file is not None and not os.path.exists(variable_file):
261276
variable_file = None
262277

263278
return_code = validator.validate_in_tool_mode(model_dictionary,
@@ -299,11 +314,16 @@ def walk(self):
299314
"discover", WlstModes.OFFLINE)
300315

301316
target_config = self.model_context.get_target_configuration()
302-
if target_config.uses_credential_secrets():
317+
if target_config.generate_script_for_secrets():
303318
target_configuration_helper.generate_k8s_script(self.model_context,
304319
self.credential_injector.get_variable_cache(),
305320
full_model_dictionary, ExceptionType.VALIDATE)
306321

322+
if target_config.generate_json_for_secrets():
323+
target_configuration_helper.generate_k8s_json(self.model_context,
324+
self.credential_injector.get_variable_cache(),
325+
full_model_dictionary)
326+
307327
# create any additional outputs from full model dictionary
308328
target_configuration_helper.create_additional_output(Model(full_model_dictionary), self.model_context,
309329
self._aliases, self.credential_injector,

core/src/main/python/wlsdeploy/json/json_translator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,12 @@ def _write_list_to_json_file(self, alist, writer, indent=''):
200200
for value in alist:
201201
writer.write(end_line)
202202
writer.println()
203-
writer.write(list_indent)
204-
writer.write(_format_json_value(value))
203+
if isinstance(value, dict):
204+
writer.write(list_indent)
205+
self._write_dictionary_to_json_file(value, writer, indent)
206+
else:
207+
writer.write(list_indent)
208+
writer.write(_format_json_value(value))
205209
end_line = ','
206210
writer.println()
207211
writer.write(indent + ']')

core/src/main/python/wlsdeploy/tool/util/targets/additional_output_helper.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ def create_additional_output(model, model_context, aliases, credential_injector,
6767

6868
# all current output types use this hash, and process a set of template files
6969
template_hash = _build_template_hash(model, model_context, aliases, credential_injector)
70-
71-
file_names = model_context.get_target_configuration().get_additional_output_types()
72-
for file_name in file_names:
73-
_create_file(file_name, template_hash, model_context, output_dir, exception_type)
70+
template_names = model_context.get_target_configuration().get_additional_output_types()
71+
for template_name in template_names:
72+
_create_file(template_name, template_hash, model_context, output_dir, exception_type)
7473

7574

7675
def _create_file(template_name, template_hash, model_context, output_dir, exception_type):

core/src/main/python/wlsdeploy/tool/util/variable_injector.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import re
77

88
import java.lang.Boolean as Boolean
9+
import java.io.FileOutputStream as FileOutputStream
10+
import java.io.FileInputStream as FileInputStream
11+
import java.util.Properties as Properties
912
import java.lang.IllegalArgumentException as IllegalArgumentException
10-
import os
13+
import os, shutil
1114

1215
import oracle.weblogic.deploy.aliases.AliasException as AliasException
1316
import oracle.weblogic.deploy.json.JsonException as JsonException
@@ -243,6 +246,18 @@ def inject_variables_keyword_file(self, append_option=None):
243246

244247
variable_dictionary = self.get_variable_cache()
245248
if variable_dictionary is not None and len(variable_dictionary) > 0:
249+
# change variable_file_location to output_dir for target operation
250+
if self.__model_context.get_target() is not None:
251+
new_variable_file_location = os.path.join(self.__model_context.get_output_dir(),
252+
os.path.basename(variable_file_location))
253+
if variable_file_location is not None and os.path.exists(variable_file_location):
254+
# copy the original file first
255+
append = True
256+
shutil.copyfile(variable_file_location, new_variable_file_location)
257+
self._filter_duplicate_properties(new_variable_file_location, variable_dictionary)
258+
259+
variable_file_location = new_variable_file_location
260+
246261
variables_inserted = self._write_variables_file(variable_dictionary, variable_file_location, append)
247262
if variables_inserted:
248263
_logger.info('WLSDPLY-19518', variable_file_location, class_name=_class_name,
@@ -255,6 +270,28 @@ def inject_variables_keyword_file(self, append_option=None):
255270
_logger.exiting(class_name=_class_name, method_name=_method_name, result=variables_inserted)
256271
return variables_inserted, return_model, variable_file_location
257272

273+
274+
def _filter_duplicate_properties(self, variable_file_location, variable_dictionary):
275+
_method_name = '_filter_duplicate_property'
276+
_logger.entering(class_name=_class_name, method_name=_method_name)
277+
try:
278+
fis = FileInputStream(variable_file_location)
279+
prop = Properties()
280+
prop.load(fis)
281+
fis.close()
282+
for key in variable_dictionary:
283+
if prop.get(key) is not None:
284+
prop.remove(key)
285+
fos = FileOutputStream(variable_file_location)
286+
prop.store(fos, None)
287+
fos.close()
288+
except IOException, e:
289+
_logger.warning('WLSDPLY-05803', e.getLocalizedMessage(),
290+
class_name=_class_name, method_name=_method_name)
291+
292+
_logger.exiting(class_name=_class_name, method_name=_method_name)
293+
294+
258295
def inject_variables_keyword_dictionary(self, injector_file_list):
259296
"""
260297
Takes a variable keyword dictionary and returns a variables for file in a dictionary

core/src/main/python/wlsdeploy/util/target_configuration.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
# types for credential method
99
CREDENTIALS_METHOD = "credentials_method"
10+
CREDENTIALS_OUTPUT_METHOD = "credentials_output_method"
1011

1112
# Overrides the Kubernetes secret name for the WebLogic admin user credential
1213
WLS_CREDENTIALS_NAME = "wls_credentials_name"
@@ -46,6 +47,13 @@ def get_credentials_method(self):
4647
"""
4748
return dictionary_utils.get_element(self.config_dictionary, CREDENTIALS_METHOD)
4849

50+
def get_credentials_output_method(self):
51+
"""
52+
Returns the method for generating secrets creation method.
53+
:return: script or json
54+
"""
55+
return dictionary_utils.get_element(self.config_dictionary, CREDENTIALS_OUTPUT_METHOD)
56+
4957
def get_wls_credentials_name(self):
5058
"""
5159
Returns the method for handling credentials in the model.
@@ -101,6 +109,21 @@ def uses_credential_secrets(self):
101109
"""
102110
return self.get_credentials_method() in [SECRETS_METHOD, CONFIG_OVERRIDES_SECRETS_METHOD]
103111

112+
def generate_script_for_secrets(self):
113+
"""
114+
Determine if it needs to generate shell script for creating secrets.
115+
:return: True if it is not equal to json
116+
"""
117+
return not self.get_credentials_output_method() in ['json']
118+
119+
120+
def generate_json_for_secrets(self):
121+
"""
122+
Determine if it needs to generate json file for creating secrets.
123+
:return: True if generating json file, False otherwise
124+
"""
125+
return self.get_credentials_output_method() in ['json']
126+
104127
def manages_credentials(self):
105128
"""
106129
Determine if this configuration manages credential values in the model.

core/src/main/python/wlsdeploy/util/target_configuration_helper.py

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
from wlsdeploy.tool.util.targets import file_template_helper
2323
from wlsdeploy.util import dictionary_utils
2424
from wlsdeploy.util.cla_utils import CommandLineArgUtil
25+
from wlsdeploy.json.json_translator import PythonToJson
26+
2527

2628
__class_name = 'target_configuration_helper'
2729
__logger = PlatformLogger('wlsdeploy.tool.util')
@@ -66,6 +68,7 @@
6668
SECURITY_NM_REPLACEMENT = 'SecurityConfig.NodeManager.'
6769

6870
K8S_SCRIPT_NAME = 'create_k8s_secrets.sh'
71+
K8S_SECRET_JSON_NAME = 'k8s_secrets.json'
6972
K8S_SCRIPT_RESOURCE_PATH = 'oracle/weblogic/deploy/k8s/' + K8S_SCRIPT_NAME
7073

7174

@@ -88,20 +91,8 @@ def process_target_arguments(argument_map):
8891
__logger.throwing(ex, class_name=__class_name, method_name=_method_name)
8992
raise ex
9093

91-
# Set the -variable_file parameter if not present with default
92-
if CommandLineArgUtil.VARIABLE_FILE_SWITCH not in argument_map:
93-
path = os.path.join(output_dir, target_name + "_variable.properties")
94-
argument_map[CommandLineArgUtil.VARIABLE_FILE_SWITCH] = path
9594

96-
97-
def generate_k8s_script(model_context, token_dictionary, model_dictionary, exception_type):
98-
"""
99-
Generate a shell script for creating k8s secrets.
100-
:param model_context: used to determine output directory
101-
:param token_dictionary: contains every token
102-
:param model_dictionary: used to determine domain UID
103-
:param exception_type: type of exception to throw
104-
"""
95+
def _prepare_k8s_secrets(model_context, token_dictionary, model_dictionary):
10596

10697
# determine the domain name and UID
10798
topology = dictionary_utils.get_dictionary_element(model_dictionary, TOPOLOGY)
@@ -165,13 +156,75 @@ def generate_k8s_script(model_context, token_dictionary, model_dictionary, excep
165156
{'text': exception_helper.get_message('WLSDPLY-01670')}
166157
]
167158
script_hash['longMessageDetails'] = long_messages
159+
return script_hash
168160

161+
162+
def generate_k8s_script(model_context, token_dictionary, model_dictionary, exception_type):
163+
"""
164+
Generate a shell script for creating k8s secrets.
165+
:param model_context: used to determine output directory
166+
:param token_dictionary: contains every token
167+
:param model_dictionary: used to determine domain UID
168+
:param exception_type: type of exception to throw
169+
"""
170+
script_hash = _prepare_k8s_secrets(model_context, token_dictionary, model_dictionary)
169171
file_location = model_context.get_output_dir()
170172
k8s_file = File(file_location, K8S_SCRIPT_NAME)
171173
file_template_helper.create_file_from_resource(K8S_SCRIPT_RESOURCE_PATH, script_hash, k8s_file, exception_type)
172174
FileUtils.chmod(k8s_file.getPath(), 0750)
173175

174176

177+
def generate_k8s_json(model_context, token_dictionary, model_dictionary):
178+
"""
179+
Generate a json file.
180+
:param model_context: used to determine output directory
181+
:param token_dictionary: contains every token
182+
:param model_dictionary: used to determine domain UID
183+
:param exception_type: type of exception to throw
184+
"""
185+
script_hash = _prepare_k8s_secrets(model_context, token_dictionary, model_dictionary)
186+
187+
file_location = model_context.get_output_dir()
188+
k8s_file = os.path.join(file_location, K8S_SECRET_JSON_NAME)
189+
result = _build_json_secrets_result(script_hash)
190+
json_object = PythonToJson(result)
191+
json_object.write_to_json_file(k8s_file)
192+
193+
194+
def _build_json_secrets_result(script_hash):
195+
196+
result = {}
197+
secrets_array = []
198+
199+
for node in script_hash['secrets']:
200+
secret = {}
201+
for item in ['secretName', 'comments']:
202+
secret[item] = node[item]
203+
secret['keys'] = {}
204+
secret['keys']['password'] = ""
205+
secrets_array.append(secret)
206+
207+
for node in script_hash['pairedSecrets']:
208+
secret = {}
209+
for item in ['secretName', 'comments']:
210+
secret[item] = node[item]
211+
secret['keys'] = {}
212+
secret['keys']['password'] = ""
213+
secret['keys']['username'] = node['user']
214+
# For ui, empty it now.
215+
if secret['keys']['username'].startswith('@@SECRET:'):
216+
secret['keys']['username'] = ""
217+
if secret['secretName'] == 'weblogic-credentials':
218+
secret['keys']['username'] = ""
219+
secrets_array.append(secret)
220+
221+
result['secrets'] = secrets_array
222+
result['domainUID'] = script_hash['domainUid']
223+
result['namespace'] = script_hash['namespace']
224+
225+
return result
226+
227+
175228
def format_as_secret_token(secret_id, target_config):
176229
"""
177230
Format the secret identifier as an @@SECRET token for use in a model.

core/src/main/python/wlsdeploy/util/variables.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ def _substitute(text, variables, model_context, attribute_name=None):
204204
:return: the replaced text
205205
"""
206206
method_name = '_substitute'
207+
validation_method = model_context.get_validation_method()
208+
target_configuration = model_context.get_target_configuration()
209+
if target_configuration:
210+
validation_method = target_configuration.get_validation_method()
207211

208212
# skip lookups for text with no @@
209213
if '@@' in text:
@@ -213,7 +217,8 @@ def _substitute(text, variables, model_context, attribute_name=None):
213217
for token, key in matches:
214218
# log, or throw an exception if key is not found.
215219
if key not in variables:
216-
_report_token_issue('WLSDPLY-01732', method_name, model_context, key)
220+
if validation_method != 'lax':
221+
_report_token_issue('WLSDPLY-01732', method_name, model_context, key)
217222
continue
218223

219224
value = variables[key]
@@ -224,7 +229,8 @@ def _substitute(text, variables, model_context, attribute_name=None):
224229
for token, key in matches:
225230
# log, or throw an exception if key is not found.
226231
if not os.environ.has_key(key):
227-
_report_token_issue('WLSDPLY-01737', method_name, model_context, key)
232+
if validation_method != 'lax':
233+
_report_token_issue('WLSDPLY-01737', method_name, model_context, key)
228234
continue
229235

230236
value = os.environ.get(key)
@@ -234,10 +240,13 @@ def _substitute(text, variables, model_context, attribute_name=None):
234240
matches = _secret_pattern.findall(text)
235241
for token, name, key in matches:
236242
value = _resolve_secret_token(name, key, model_context)
243+
237244
if value is None:
238-
secret_token = name + ':' + key
239-
known_tokens = _list_known_secret_tokens()
240-
_report_token_issue('WLSDPLY-01739', method_name, model_context, secret_token, known_tokens)
245+
# does not match, only report for non target case
246+
if validation_method != 'lax':
247+
secret_token = name + ':' + key
248+
known_tokens = _list_known_secret_tokens()
249+
_report_token_issue('WLSDPLY-01739', method_name, model_context, secret_token, known_tokens)
241250
continue
242251

243252
text = text.replace(token, value)
@@ -260,7 +269,7 @@ def _substitute(text, variables, model_context, attribute_name=None):
260269

261270
# if any @@TOKEN: remains in the value, throw an exception
262271
matches = _unresolved_token_pattern.findall(text)
263-
if matches:
272+
if matches and validation_method != 'lax':
264273
match = matches[0]
265274
token = match[1]
266275
sample = "@@" + token + ":<name>"

core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ WLSDPLY-05716=The Security Configuration Provider at location {0} has a differen
506506

507507
# prepare_model.py
508508
WLSDPLY-05801=Error in prepare model {0}
509+
WLSDPLY-05802=Model file name {0} must not be named as one of these names: {1} when -target {2} is specified.
510+
WLSDPLY-05803=Failed to filter original variable file {0}, variable file in output directory may have duplicates.
509511

510512
###############################################################################
511513
# Message number 06000 - 07999 Discover #

core/src/main/targetconfigs/k8s/target.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
},
77
"variable_injectors" : {"PORT": {},"HOST": {},"URL": {}},
88
"validation_method" : "lax",
9-
"credentials_method" : "secrets"
9+
"credentials_method" : "secrets",
10+
"credentials_output_method" : "script"
1011
}

0 commit comments

Comments
 (0)