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

Migrate eso-client to nexpose-client / CSRF header update #309

Merged
merged 19 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
617cedf
Port eso-client gem to nexpose-client
hdub-tech Dec 6, 2017
e87cb24
NEX-50812: Add X-Requested-With header to eso and nexpose
hdub-tech Dec 6, 2017
ae66880
store Array properties differently
smorris-r7 Dec 14, 2017
534d2bd
Appease most of the houndci-bot complaints
hdub-tech Dec 15, 2017
8ca5878
Remove eso version file
hdub-tech Dec 16, 2017
bf59a32
add nil guard in property method
smorris-r7 Dec 18, 2017
4a6394c
Merge branch 'master' into feature/hwilson/new-header-migrate-eso
hdub-tech Jan 17, 2018
a856445
Merge branch 'feature/hwilson/new-header-migrate-eso' of github.com:r…
hdub-tech Jan 17, 2018
2bdc3fe
Enhanced AWSv2 ARN messages
hdub-tech Jan 18, 2018
116a77f
Document more Error messages returned by the ConfigManager
hdub-tech Jan 30, 2018
fabee7b
Merge branch 'master' into feature/hwilson/new-header-migrate-eso
gschneider-r7 Jun 1, 2018
e044c54
QE-XXXX: Required fixes for AWS configs w/ array props
hdub-tech Jul 13, 2018
3d5f269
Merge branch 'feature/hwilson/new-header-migrate-eso' of github.com:r…
hdub-tech Jul 13, 2018
ef65ef0
QE-XXXX: Add ability to delete a property on a Configuration
hdub-tech Jul 18, 2018
381d054
Adds support for AWS asset+tag sync
hdub-tech Jul 28, 2018
cc1e8a7
QE-587: Add verify-aws-target Int Option support
hdub-tech Aug 8, 2018
b678187
QE-587: Missed a previous_type_name for verify_aws_targets
hdub-tech Aug 9, 2018
4425aff
Non-required change which makes the JSON for configs consistent
hdub-tech Aug 27, 2018
091e546
Merge branch 'master' into feature/hwilson/new-header-migrate-eso
smorris-r7 Feb 4, 2020
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
23 changes: 23 additions & 0 deletions lib/eso.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'date'
require 'json'
require 'net/http'
require 'net/https'
require 'nexpose'
require 'time'
require 'uri'

require 'eso/conductor'
require 'eso/configuration/configuration'
require 'eso/configuration/configuration_manager'
require 'eso/filter'
require 'eso/integration_option'
require 'eso/integration_options_manager'
require 'eso/nexpose'
require 'eso/service'
require 'eso/step'
require 'eso/step_configuration'
require 'eso/workflow'

module Eso

end
227 changes: 227 additions & 0 deletions lib/eso/conductor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
require 'eso/service'

module Eso
class Conductor < Service

# Constructor for Conductor.
#
# @param [String] host Hostname or IP address where this conductor resides.
# @param [Integer] port The TCP port to connect to this conductor on.
# @param [Nexpose::Connection] nsc A logged-in Nexpose::Connection object with a valid session used to authenticate.
# @return [Eso::Conductor] The newly created conductor object
#
def initialize(host:, port: 3780, nsc:)
super(host: host, port: port, nsc: nsc)
@url = "https://#{@host}:#{@port}/eso/conductor-service/api/"
end

# Return all of the workflows that currently exist on this conductor.
#
# @return [Array] An array containing all of the current workflows on the conductor in Eso::Workflow object format. Returns an empty array if no workflows are present.
#
def workflows
rv = []
json_data = get(url: "#{@url}workflows/")
json_data.each do |wf|
workflow = Workflow.new(id: wf[:id], name: wf[:name])
steps = wf[:steps]
steps.each do |step|
workflow_step = Step.new(uuid: step[:uuid],
service_name: step[:serviceName],
workflow: workflow,
type_name: step[:stepConfiguration][:typeName],
previous_type_name: step[:stepConfiguration][:previousTypeName],
configuration_params: step[:stepConfiguration][:configurationParams])
workflow.steps << workflow_step
end
rv << workflow
end
rv
end

# Return the workflow histories with only the state histories for the given date range.
#
# @param [Fixnum] starttime The time in milliseconds since epoch for which you want the workflow histories
# @param [Fixnum] endtime The time in milliseconds since epoch for which you want the workflow histories
# @return [Array[Eso::Workflow::History]] An array containing all of the workflow histories from the
# Conductor, which has StateHistory objects containing startTime's within the specified time range. Only the
# StateHistories within that range are returned in the WorkflowHistory object. Returns an empty array if none are present.
def workflow_histories(starttime, endtime)
histories = []
json_data = get(url: "#{@url}workflows/history/#{starttime}/#{endtime}")
json_data.each do |wf|
# Initialize WorkflowHistory elements
workflow_steps = []
state_histories = []

# Create a new WorkflowHistory with the details we already know
workflow_history = Eso::Workflow::History.new(id: wf[:id],
name: wf[:name],
timeCreated: wf[:timeCreated],
state: wf[:state],
message: wf[:message],
steps: workflow_steps,
history: state_histories
)

# Parse the steps out of the response to be returned with the WorkflowHistory
wf[:steps].each do |step|
workflow_steps << Step.new(uuid: step[:uuid],
service_name: step[:serviceName],
workflow: workflow_history,
type_name: step[:stepConfiguration][:typeName],
previous_type_name: step[:stepConfiguration][:previousTypeName],
configuration_params: step[:stepConfiguration][:configurationParams])
end
workflow_history.steps = workflow_steps

# Parse the histories out of the response, to be returned with the WorkflowHistory. For some reason.
# this failed with named parameters. For now I returned it to positional.
wf[:history].each do |history|
state_histories << Eso::Workflow::StateHistory.new(history[:message],
history[:state],
history[:startTime])
end
workflow_history.state_histories = state_histories

# Add the Workflow History we just built to the list to be returned.
histories << workflow_history
end
histories
end

# Get the state of the specified workflow.
#
# @param [String] workflow_id The ID of the workflow to retrieve the state of.
# @return [String] The current state of the workflow.
#
def workflow_state(workflow_id:)
get(url: "#{@url}workflows/#{workflow_id}/state")
end

# Get the count of items in a state of the specified workflow.
#
# @param [Eso::Workflow::State] state The state of the workflows to retrieve the count of.
# @return [Integer] The number of workflows in the requested state.
#
def workflows_state_count(state)
get(url: "#{@url}workflows/count/#{state}")
end

# Retrieve the states for all of the workflows created on the conductor.
#
# @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
#
def workflow_states
wfs = workflows
states = {}
wfs.each { |wf| states[wf.id] = workflow_state(workflow_id: wf.id) }
states
end

# Create a new workflow on this conductor.
#
# @param [String] name The user-facing name the workflow will be created with.
# @param [Array] steps An array containing each of the steps that the workflow will be created with, in Eso::Step format.
# @return [Eso::Workflow] The newly created workflow object
#
def create_workflow(name:, steps:)
workflow = Workflow.new(name: name, steps: steps)

resp = post(url: "#{@url}workflows/", payload: workflow.to_json)
created_workflow = Workflow.load(self, resp[:id])

created_workflow
end

# Update an existing workflow on the conductor to have the configuration of the workflow object passed into this method.
#
# @param [Eso::Workflow] updated_workflow A workflow object that has already had all required changes made to it. This workflow must have an ID set.
#
def update_workflow(updated_workflow:)
payload = updated_workflow.to_json
put(url: "#{@url}workflows/#{updated_workflow.id}", payload: payload)
end

# Delete an existing workflow from the conductor.
#
# @param [String] workflow_id The ID of the workflow to be deleted.
#
def delete_workflow(workflow_id:)
delete(url: "#{@url}workflows/#{workflow_id}")
end

# Delete all current workflows on the conductor.
#
def delete_all_workflows
wfs = workflows
wfs.each { |wf| delete_workflow(workflow_id: wf.id) }
end

# Start the specified workflow.
#
# @param [String] workflow_id The ID of the workflow to be started.
#
def start_workflow(workflow_id:)
post(url: "#{@url}workflows/#{workflow_id}/state")
end

# Stop the specified workflow.
#
# @param [String] workflow_id The ID of the workflow to be stopped.
#
def stop_workflow(workflow_id:)
delete(url: "#{@url}workflows/#{workflow_id}/state")
end

# Start all workflows that exist on the conductor.
#
# @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
#
def start_all_workflows
wf_states = workflow_states

wf_states.each { |wf_id, state| start_workflow(workflow_id: wf_id) if state[:workflow_state] == "STOPPED" }
workflow_states
end

# Stop all workflows that exist on the conductor.
#
# @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
#
def stop_all_workflows
wf_states = workflow_states

wf_states.each { |wf_id, state| stop_workflow(workflow_id: wf_id) if state[:workflow_state] == "RUNNING" }
workflow_states
end

# Returns the translated value of the specified key for a step type (defined in Eso::StepNames).
# The translated value will be based on the language settings the user has configured.
#
# @param [String] step_type The step type to query metadata for. Valid values defined in Eso::StepNames
# @param [String] key The key value in the metadata that maps to the desired label.
# @return [String] The translated value of the key.
#
def get_translation_label(step_type, key)
json_data = get(url: "#{@url}services/nexpose/metadata/#{step_type}")

target_hash = json_data[:labels].values.find { |label_hash| label_hash.has_key?(key) }
target_hash[key] if target_hash
end

# Returns the metadata key for a specified translated string.
# The translated value needs to be in language that the user has configured.
#
# @param [String] step_type The step type to query metadata for. Valid values defined in Eso::StepNames
# @param [String] label The translated value of which you are requesting the key for.
# @return [String] The metadata key corresponding to this label.
#
def get_translation_key(step_type, label)
json_data = get(url: "#{@url}services/nexpose/metadata/#{step_type}")

target_hash = json_data[:labels].values.find { |label_hash| label_hash.values.include?(label) }
target_hash.key(label).to_s if target_hash
end
end
end
117 changes: 117 additions & 0 deletions lib/eso/configuration/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
module Eso
# This class represents the Configuration that is sent to the server for new
# style Discovery Connections.
class Configuration
attr_accessor :service_name, :config_name, :config_id, :properties

def initialize(service_name:, config_name:, properties:[], config_id:)
@service_name = service_name
@config_name = config_name
@properties = properties
@config_id = config_id
end

# Convert the Configuration to a JSON string for sending to Nexpose
#
# @return [String] A JSON String representation of the Configuration
def to_json
self.to_hash.to_json
end

# Convert the Configuration to a Hash
#
# @return [Hash] A Hash representation of the Configuration
def to_hash
hash = {:configId => @config_id,
:serviceName => @service_name,
:configName => @config_name,
:configurationAttributes => {:valueClass => Eso::Values::OBJECT,
:objectType => 'service_configuration',
:properties => []}}
properties.each {|prop| hash[:configurationAttributes][:properties] << prop.to_hash}
end

# Retrieve a Configuration attribute property value given the name of the property
#
# @param [String] name The name of the property to retrieve
# @return [String] The value of the property
def property(name)
prop = properties.find{|attr| attr.property == name}
prop.value unless prop.nil?
end

# Update a Configuration attribute property value given the name of the property
#
# @param [String] name The name of the property to update
# @param [String] value The value of the property to update
# @return [String] The value of the property
def update_property(name, value)
properties.find{|attr| attr.property == name}.value = value
end

# Load a Configuration object from a Hash
#
# @param [Hash] hash The Hash containing the Configuration object
# @return [Configuration] The Configuration object which was in the Hash
def self.load(hash)
configuration = self.new(service_name: hash[:serviceName],
config_name: hash[:configName],
config_id: hash[:configID])
hash[:configurationAttributes][:properties].each do |prop|
configuration.properties << ConfigurationAttribute.load(prop)
end
configuration
end
end

# The ConfigurationAttribute is a property of the Configuration
class ConfigurationAttribute
attr_accessor :property, :value_class, :value

def initialize(property, value_class, value)
@property = property
@value_class = value_class
@value = value
end

# Convert the ConfigurationAttribute to a JSON string for sending to Nexpose
#
# @return [String] A JSON String representation of the ConfigurationAttribute
def to_json
self.to_hash.to_json
end

# Convert the ConfigurationAttribute to a Hash
#
# @return [Hash] A Hash representation of the ConfigurationAttribute
def to_hash
prop = @property.to_sym
hash = {prop => {}}
hash[prop]['valueClass'] = @value_class
if @value_class == Eso::Values::ARRAY
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the part that was broken when Nexpose cukes tried to use it.

items = []
@value.each{|v| items<< {'value' => v, 'valueClass' => Eso::Values::STRING}}
hash[prop]['items'] = items
else
hash[prop]['value'] = @value
end
hash
end

# Load a ConfigurationAttribute object from an Array
#
# @param [Array] array The Array containing the ConfigurationAttribute object
# @return [ConfigurationAttribute] The ConfigurationAttribute object which was in the Array
def self.load(array)
property = array.first
value_class = array.last['valueClass']
value =
if value_class == Eso::Values::ARRAY
array.last['items'].map{|item| item['value']}
else
array.last['value']
end
self.new(property, value_class, value)
end
end
end
Loading