Module: RubySDK

Defined in:
lib/rubysdk.rb

Defined Under Namespace

Classes: GRPCServer

Class Method Summary collapse

Class Method Details

.Serve(jobs) ⇒ Object

Serve caches the given jobs and starts the gRPC server. This function should be last called in the plugin main function.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/rubysdk.rb', line 77

def self.Serve(jobs)
  # Cache the jobs for later processing.
  # We have to transform given jobs into suitable proto models.
  cached_jobs = []
  jobs.each do |job|
    # Transform manual interaction
    manual_interaction = nil
    if job.interaction != nil
      manual_interaction = Interface::ManualInteraction.new(description: job.interaction.desc,
                                                 type: job.interaction.type,
                                                 value: job.interaction.value)
    end

    # Transform arguments
    args = []
    if job.args != nil
      job.args.each do |arg|
        # Description and Value are optional.
        # Set default values for those.
        arg.desc = "" if arg.desc == nil
        arg.value = "" if arg.value == nil
          
        trans_arg = Proto::Argument.new(description: arg.desc,
                                        type: arg.type,
                                        key: arg.key,
                                        value: arg.value)

        args.push trans_arg
      end
    end

    # Create proto job object
    proto_job = Proto::Job.new(unique_id: FNV.new.fnv1a_32(job.title),
                               title: job.title,
                               description: job.title,
                               args: args,
                               interaction: manual_interaction)

    # Resolve job dependencies
    if job.dependson != nil
      proto_job.dependson = Google::Protobuf::RepeatedField.new(:uint32, [])
      job.dependson.each do |dep_job|
        dep_found = false
        jobs.each do |curr_job|
          if curr_job.title.casecmp(dep_job) == 0
            proto_job.dependson.push FNV.new.fnv1a_32(curr_job.title)
            dep_found = true
            break
          end
        end
        
        raise "job #{job.title} has dependency #{dep_job} which is not declared" unless dep_found == true
      end
    end

    # Create wrapper object
    wrapper_job = Interface::JobsWrapper.new(handler: job.handler, job: proto_job)
    cached_jobs.push wrapper_job
  end

  # Check if two jobs have the same title which is restricted.
  dup_map = {}
  cached_jobs.each do |job|
    dup_map[job.job.unique_id] = (dup_map[job.job.unique_id] || 0) + 1

    if dup_map[job.job.unique_id] > 1
      raise "duplicate job with the title #{job.title} found which is not allowed"
    end
  end

  # Get certificates path from env variables.
  cert_path = ENV["GAIA_PLUGIN_CERT"]
  key_path = ENV["GAIA_PLUGIN_KEY"]
  root_ca_path = ENV["GAIA_PLUGIN_CA_CERT"]

  # Check if variable is empty.
  raise "GAIA_PLUGIN_CERT not set" unless cert_path
  raise "GAIA_PLUGIN_KEY not set" unless key_path
  raise "GAIA_PLUGIN_CA_CERT not set" unless root_ca_path

  # Check if all certs are available.
  raise "cannot find path to certificate" unless File.file?(cert_path)
  raise "cannot find path to key" unless File.file?(key_path)
  raise "cannot find path to root CA certificate" unless File.file?(root_ca_path)

  # Implement health service.
  health_svc = Grpc::Health::Checker.new
  health_svc.add_status("plugin", Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING)

  # Load certificates and create credentials.
  credentials = GRPC::Core::ServerCredentials.new(
    File.read(root_ca_path),
    [{
      private_key: File.read(key_path),
      cert_chain: File.read(cert_path)
    }],
    true # force client authentication.
  )

  # Register gRPC server and handle.
  host = '127.0.0.1'
  s = GRPC::RpcServer.new
  port = s.add_http2_port(host+':0', credentials)
  s.handle(GRPCServer.new(cached_jobs))
  s.handle(health_svc)

  # Output the address and service name to stdout.
  # hashicorp go-plugin will use that to establish a connection.
  STDOUT.puts "1|2|tcp|#{host}:#{port}|grpc"

  s.run_till_terminated
end