Update documentation for DMaaP Mediator Producer 52/7552/8
authorelinuxhenrik <henrik.b.andersson@est.tech>
Wed, 12 Jan 2022 15:12:45 +0000 (16:12 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Wed, 19 Jan 2022 06:51:43 +0000 (07:51 +0100)
Issue-ID: NONRTRIC-702
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Change-Id: I6b9115510a4a965f253d69ec4bdf680416d5644d

19 files changed:
dmaap-mediator-producer/README.md
dmaap-mediator-producer/configs/typeSchemaDmaap.json
dmaap-mediator-producer/configs/typeSchemaKafka.json
dmaap-mediator-producer/docs/docs.go [new file with mode: 0644]
dmaap-mediator-producer/docs/swagger.json [new file with mode: 0644]
dmaap-mediator-producer/docs/swagger.yaml [new file with mode: 0644]
dmaap-mediator-producer/generate_swagger_docs.sh [new file with mode: 0755]
dmaap-mediator-producer/go.mod
dmaap-mediator-producer/go.sum
dmaap-mediator-producer/internal/config/config_test.go
dmaap-mediator-producer/internal/jobs/jobs_test.go
dmaap-mediator-producer/internal/server/server.go
dmaap-mediator-producer/main.go
dmaap-mediator-producer/main_test.go [new file with mode: 0644]
docs/api-docs.rst
docs/conf.py
docs/developer-guide.rst
docs/installation-guide.rst
docs/overview.rst

index 7cb1919..6009a8f 100644 (file)
@@ -14,7 +14,13 @@ The producer takes a number of environment variables, described below, as config
 >- PRODUCER_KEY_PATH   Optional. The path to the key to the certificate to use for https.         Defaults to `security/producer.key`
 >- LOG_LEVEL           Optional. The log level, which can be `Error`, `Warn`, `Info` or `Debug`.  Defaults to `Info`.
 
-The file `configs/type_config.json` contains the configuration of job types that the producer will support.
+Any of the addresses used by this product can be configured to use https, by specifying it as the scheme of the address URI. Clients configured to use https will not use server certificate verification. The communication towards the consumers will use https if their callback address URI uses that scheme. The producer's own callback will only listen to the scheme configured in the scheme of the info producer host address.
+
+The configured public key and cerificate shall be PEM-encoded. A self signed certificate and key are provided in the `security` folder of the project. These files should be replaced for production. To generate a self signed key and certificate, use the example code below:
+
+    openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+
+The file `configs/type_config.json` contains the configuration of job types that the producer will support, see example below.
 
     {
        "types":
@@ -22,36 +28,97 @@ The file `configs/type_config.json` contains the configuration of job types that
           {
             "id": The ID of the job type, e.g. "STD_Fault_Messages",
             "dmaapTopicUrl": The topic URL to poll from DMaaP Message Router, e.g. "events/unauthenticated.SEC_FAULT_OUTPUT/dmaapmediatorproducer/STD_Fault_Messages"
+          },
+          {
+            "id": The ID of the job type, e.g. "Kafka_TestTopic",
+            "kafkaInputTopic": The Kafka topic to poll
           }
       ]
     }
 
-Any of the addresses used by this product can be configured to use https, by specifying it as the scheme of the address URI. Clients configured to use https will not use server certificate verification. The communication towards the consumers will use https if their callback address URI uses that scheme. The producer's own callback will only listen to the scheme configured in the scheme of the info producer host address.
+Each information type has the following properties:
+ - id the information type identity as exposed in the Information Coordination Service data consumer API
+ - dmaapTopicUrl the URL to for fetching information from  DMaaP
+ - kafkaInputTopic the Kafka topic to get input from
 
-The configured public key and cerificate shall be PEM-encoded. A self signed certificate and key are provided in the `security` folder of the project. These files should be replaced for production. To generate a self signed key and certificate, use the example code below:
-
-    openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+Either the "dmaapTopicUrl" or the "kafkaInputTopic" must be provided for each type, not both.
 
 ## Functionality
 
 At start up the producer will register the configured job types in ICS and also register itself as a producer supporting these types. If ICS is unavailable, the producer will retry to connect indefinetely. The same goes for MR.
 
-Once the initial registration is done, the producer will constantly poll MR for all configured job types. When receiving messages for a type, it will distribute these messages to all jobs registered for the type. If no jobs for that type are registered, the messages will be discarded. If a consumer is unavailable for distribution, the messages will be discarded for that consumer until it is available again.
+Once the initial registration is done, the producer will constantly poll MR and/or Kafka for all configured job types. When receiving messages for a type, it will distribute these messages to all jobs registered for the type. If no jobs for that type are registered, the messages will be discarded. If a consumer is unavailable for distribution, the messages will be discarded for that consumer until it is available again.
 
 The producer provides a REST API that fulfills the ICS Data producer API, see [Data producer (callbacks)](<https://docs.o-ran-sc.org/projects/o-ran-sc-nonrtric/en/latest/ics-api.html#tag/Data-producer-(callbacks)>). The health check method returns the registration status of the producer in ICS as JSON. It also provides a method to control the log level of the producer. The available log levels are the same as the ones used in the configuration above.
 
     PUT https://mrproducer:8085/admin/log?level=<new level>
 
+The Swagger documentation of the producer's API is also available, through the `/swagger` path.
+
+When an Information Job is created in the Information Coordinator Service Consumer API, it is possible to define a number of job specific properties. For an Information type that has a Kafka topic defined, the following Json schema defines the properties that can be used:
+
+
+```sh
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "type": "object",
+  "properties": {
+    "bufferTimeout": {
+      "type": "object",
+      "properties": {
+        "maxSize": {
+          "type": "integer"
+        },
+        "maxTimeMiliseconds": {
+          "type": "integer"
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "maxSize",
+        "maxTimeMiliseconds"
+      ]
+    }
+  },
+  "additionalProperties": false
+}
+```
+-bufferTimeout, can be used to reduce the number of REST calls to the consumer. If defined, a number of objects will be
+ buffered and sent in one REST call to the consumer.
+ The buffered objects will be put in a Json array and quoted. Example;
+   Object1 and Object2 may be posted in one call -->  ["Object1", "Object2"]
+ The bufferTimeout is a Json object and the parameters in the object are:
+   - maxSize the maximum number of buffered objects before posting
+   - maxTimeMiliseconds the maximum delay time to buffer before posting
+ If no bufferTimeout is specified, each object will be posted as received in separate calls (not quoted and put in a Json array).
+
+
+For an information type that only has a DMaaP topic, the following Json schema is used:
+
+```sh
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "type": "object",
+  "properties": {
+  },
+  "additionalProperties": false
+}
+
 ## Development
 
-To make it easy to test during development of the producer, two stubs are provided in the `stub` folder.
+To make it easy to test during development of the producer, three stubs are provided in the `stub` folder.
 
 One, under the `dmaap` folder, called `dmaap` that stubs MR and respond with an array with one message with `eventSeverity` alternating between `NORMAL` and `CRITICAL`. The default port is `3905`, but this can be overridden by passing a `-port <PORT>` flag when starting the stub. To build and start the stub, do the following:
 >1. cd stub/dmaap
 >2. go build
 >3. ./dmaap [-port \<PORT>]
 
-One, under the `consumer` folder, called `consumer` that at startup will register a job of type `STD_Fault_Messages` in ICS, and then listen for REST calls and print the body of them. By default, it listens to the port `40935`, but his can be overridden by passing a `-port <PORT>` flag when starting the stub. To build and start the stub, do the following:
+An ICS stub, under the `ics` folder, that listens for registration calls from the producer. When it gets a call it prints out the data of the call. By default, it listens to the port `8434`, but his can be overridden by passing a `-port [PORT]` flag when starting the stub. To build and start the stub, do the following:
+>1. cd stub/ics
+>2. go build [-port \<PORT>]
+>3. ./ics
+
+One, under the `consumer` folder, called `consumer` that at startup will register a job of type `STD_Fault_Messages` in ICS, if it is available, and then listen for REST calls and print the body of them. By default, it listens to the port `40935`, but his can be overridden by passing a `-port <PORT>` flag when starting the stub. To build and start the stub, do the following:
 >1. cd stub/consumer
 >2. go build
 >3. ./consumer [-port \<PORT>]
index a50b236..4abee49 100644 (file)
@@ -2,9 +2,6 @@
   "$schema": "http://json-schema.org/draft-04/schema#",
   "type": "object",
   "properties": {
-    "filter": {
-       "type": "string"
-     }
   },
   "additionalProperties": false
 }
index dcd40f9..9c3980f 100644 (file)
@@ -2,9 +2,6 @@
   "$schema": "http://json-schema.org/draft-04/schema#",
   "type": "object",
   "properties": {
-    "filter": {
-      "type": "string"
-    },
     "bufferTimeout": {
       "type": "object",
       "properties": {
diff --git a/dmaap-mediator-producer/docs/docs.go b/dmaap-mediator-producer/docs/docs.go
new file mode 100644 (file)
index 0000000..823df03
--- /dev/null
@@ -0,0 +1,248 @@
+// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// This file was generated by swaggo/swag
+package docs
+
+import (
+       "bytes"
+       "encoding/json"
+       "strings"
+       "text/template"
+
+       "github.com/swaggo/swag"
+)
+
+var doc = `{
+    "schemes": {{ marshal .Schemes }},
+    "swagger": "2.0",
+    "info": {
+        "description": "{{escape .Description}}",
+        "title": "{{.Title}}",
+        "contact": {},
+        "license": {
+            "name": "Apache 2.0",
+            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        },
+        "version": "{{.Version}}"
+    },
+    "host": "{{.Host}}",
+    "basePath": "{{.BasePath}}",
+    "paths": {
+        "/admin/log": {
+            "put": {
+                "description": "Set the log level of the producer.",
+                "tags": [
+                    "Admin"
+                ],
+                "summary": "Set log level",
+                "parameters": [
+                    {
+                        "enum": [
+                            "Error",
+                            "Warn",
+                            "Info",
+                            "Debug"
+                        ],
+                        "type": "string",
+                        "description": "string enums",
+                        "name": "level",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/health_check": {
+            "get": {
+                "description": "Get the status of the producer. Will show if the producer has registered in ICS.",
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Get status",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/info_job": {
+            "post": {
+                "description": "Callback for ICS to add an info job",
+                "consumes": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Add info job",
+                "parameters": [
+                    {
+                        "description": "Info job data",
+                        "name": "user",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/jobs.JobInfo"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/info_job/{infoJobId}": {
+            "delete": {
+                "description": "Callback for ICS to delete an info job",
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Delete info job",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Info job ID",
+                        "name": "infoJobId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/swagger": {
+            "get": {
+                "description": "Get the Swagger API documentation for the producer.",
+                "tags": [
+                    "Admin"
+                ],
+                "summary": "Get Swagger Documentation",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        }
+    },
+    "definitions": {
+        "jobs.BufferTimeout": {
+            "type": "object",
+            "properties": {
+                "maxSize": {
+                    "type": "integer"
+                },
+                "maxTimeMiliseconds": {
+                    "type": "integer"
+                }
+            }
+        },
+        "jobs.JobInfo": {
+            "type": "object",
+            "properties": {
+                "info_job_data": {
+                    "$ref": "#/definitions/jobs.Parameters"
+                },
+                "info_job_identity": {
+                    "type": "string"
+                },
+                "info_type_identity": {
+                    "type": "string"
+                },
+                "last_updated": {
+                    "type": "string"
+                },
+                "owner": {
+                    "type": "string"
+                },
+                "target_uri": {
+                    "type": "string"
+                }
+            }
+        },
+        "jobs.Parameters": {
+            "type": "object",
+            "properties": {
+                "bufferTimeout": {
+                    "$ref": "#/definitions/jobs.BufferTimeout"
+                }
+            }
+        }
+    }
+}`
+
+type swaggerInfo struct {
+       Version     string
+       Host        string
+       BasePath    string
+       Schemes     []string
+       Title       string
+       Description string
+}
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = swaggerInfo{
+       Version:     "1.1.0",
+       Host:        "",
+       BasePath:    "",
+       Schemes:     []string{},
+       Title:       "DMaaP Mediator Producer",
+       Description: "",
+}
+
+type s struct{}
+
+func (s *s) ReadDoc() string {
+       sInfo := SwaggerInfo
+       sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
+
+       t, err := template.New("swagger_info").Funcs(template.FuncMap{
+               "marshal": func(v interface{}) string {
+                       a, _ := json.Marshal(v)
+                       return string(a)
+               },
+               "escape": func(v interface{}) string {
+                       // escape tabs
+                       str := strings.Replace(v.(string), "\t", "\\t", -1)
+                       // replace " with \", and if that results in \\", replace that with \\\"
+                       str = strings.Replace(str, "\"", "\\\"", -1)
+                       return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1)
+               },
+       }).Parse(doc)
+       if err != nil {
+               return doc
+       }
+
+       var tpl bytes.Buffer
+       if err := t.Execute(&tpl, sInfo); err != nil {
+               return doc
+       }
+
+       return tpl.String()
+}
+
+func init() {
+       swag.Register("swagger", &s{})
+}
diff --git a/dmaap-mediator-producer/docs/swagger.json b/dmaap-mediator-producer/docs/swagger.json
new file mode 100644 (file)
index 0000000..11e030e
--- /dev/null
@@ -0,0 +1,177 @@
+{
+    "swagger": "2.0",
+    "info": {
+        "title": "DMaaP Mediator Producer",
+        "contact": {},
+        "license": {
+            "name": "Apache 2.0",
+            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        },
+        "version": "1.1.0"
+    },
+    "paths": {
+        "/admin/log": {
+            "put": {
+                "description": "Set the log level of the producer.",
+                "tags": [
+                    "Admin"
+                ],
+                "summary": "Set log level",
+                "parameters": [
+                    {
+                        "enum": [
+                            "Error",
+                            "Warn",
+                            "Info",
+                            "Debug"
+                        ],
+                        "type": "string",
+                        "description": "string enums",
+                        "name": "level",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/health_check": {
+            "get": {
+                "description": "Get the status of the producer. Will show if the producer has registered in ICS.",
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Get status",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/info_job": {
+            "post": {
+                "description": "Callback for ICS to add an info job",
+                "consumes": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Add info job",
+                "parameters": [
+                    {
+                        "description": "Info job data",
+                        "name": "user",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/jobs.JobInfo"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/info_job/{infoJobId}": {
+            "delete": {
+                "description": "Callback for ICS to delete an info job",
+                "tags": [
+                    "Data producer (callbacks)"
+                ],
+                "summary": "Delete info job",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Info job ID",
+                        "name": "infoJobId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/swagger": {
+            "get": {
+                "description": "Get the Swagger API documentation for the producer.",
+                "tags": [
+                    "Admin"
+                ],
+                "summary": "Get Swagger Documentation",
+                "responses": {
+                    "200": {
+                        "description": ""
+                    }
+                }
+            }
+        }
+    },
+    "definitions": {
+        "jobs.BufferTimeout": {
+            "type": "object",
+            "properties": {
+                "maxSize": {
+                    "type": "integer"
+                },
+                "maxTimeMiliseconds": {
+                    "type": "integer"
+                }
+            }
+        },
+        "jobs.JobInfo": {
+            "type": "object",
+            "properties": {
+                "info_job_data": {
+                    "$ref": "#/definitions/jobs.Parameters"
+                },
+                "info_job_identity": {
+                    "type": "string"
+                },
+                "info_type_identity": {
+                    "type": "string"
+                },
+                "last_updated": {
+                    "type": "string"
+                },
+                "owner": {
+                    "type": "string"
+                },
+                "target_uri": {
+                    "type": "string"
+                }
+            }
+        },
+        "jobs.Parameters": {
+            "type": "object",
+            "properties": {
+                "bufferTimeout": {
+                    "$ref": "#/definitions/jobs.BufferTimeout"
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/dmaap-mediator-producer/docs/swagger.yaml b/dmaap-mediator-producer/docs/swagger.yaml
new file mode 100644 (file)
index 0000000..501b062
--- /dev/null
@@ -0,0 +1,116 @@
+definitions:
+  jobs.BufferTimeout:
+    properties:
+      maxSize:
+        type: integer
+      maxTimeMiliseconds:
+        type: integer
+    type: object
+  jobs.JobInfo:
+    properties:
+      info_job_data:
+        $ref: '#/definitions/jobs.Parameters'
+      info_job_identity:
+        type: string
+      info_type_identity:
+        type: string
+      last_updated:
+        type: string
+      owner:
+        type: string
+      target_uri:
+        type: string
+    type: object
+  jobs.Parameters:
+    properties:
+      bufferTimeout:
+        $ref: '#/definitions/jobs.BufferTimeout'
+    type: object
+info:
+  contact: {}
+  license:
+    name: Apache 2.0
+    url: http://www.apache.org/licenses/LICENSE-2.0.html
+  title: DMaaP Mediator Producer
+  version: 1.1.0
+paths:
+  /admin/log:
+    put:
+      description: Set the log level of the producer.
+      parameters:
+      - description: string enums
+        enum:
+        - Error
+        - Warn
+        - Info
+        - Debug
+        in: query
+        name: level
+        type: string
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: Bad Request
+          schema:
+            type: string
+      summary: Set log level
+      tags:
+      - Admin
+  /health_check:
+    get:
+      description: Get the status of the producer. Will show if the producer has registered
+        in ICS.
+      responses:
+        "200":
+          description: ""
+      summary: Get status
+      tags:
+      - Data producer (callbacks)
+  /info_job:
+    post:
+      consumes:
+      - application/json
+      description: Callback for ICS to add an info job
+      parameters:
+      - description: Info job data
+        in: body
+        name: user
+        required: true
+        schema:
+          $ref: '#/definitions/jobs.JobInfo'
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: Bad Request
+          schema:
+            type: string
+      summary: Add info job
+      tags:
+      - Data producer (callbacks)
+  /info_job/{infoJobId}:
+    delete:
+      description: Callback for ICS to delete an info job
+      parameters:
+      - description: Info job ID
+        in: path
+        name: infoJobId
+        required: true
+        type: string
+      responses:
+        "200":
+          description: ""
+      summary: Delete info job
+      tags:
+      - Data producer (callbacks)
+  /swagger:
+    get:
+      description: Get the Swagger API documentation for the producer.
+      responses:
+        "200":
+          description: ""
+      summary: Get Swagger Documentation
+      tags:
+      - Admin
+swagger: "2.0"
diff --git a/dmaap-mediator-producer/generate_swagger_docs.sh b/dmaap-mediator-producer/generate_swagger_docs.sh
new file mode 100755 (executable)
index 0000000..63bc20d
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+##############################################################################
+#
+#   Copyright (C) 2022: Nordix Foundation
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+##############################################################################
+
+swag init
\ No newline at end of file
index e701c01..1a03578 100644 (file)
@@ -10,11 +10,32 @@ require (
 )
 
 require (
+       github.com/KyleBanks/depth v1.2.1 // indirect
+       github.com/PuerkitoBio/purell v1.1.1 // indirect
+       github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
        github.com/confluentinc/confluent-kafka-go v1.8.2 // indirect
+       github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
        github.com/davecgh/go-spew v1.1.1 // indirect
+       github.com/ghodss/yaml v1.0.0 // indirect
+       github.com/go-openapi/jsonpointer v0.19.5 // indirect
+       github.com/go-openapi/jsonreference v0.19.6 // indirect
+       github.com/go-openapi/spec v0.20.4 // indirect
+       github.com/go-openapi/swag v0.19.15 // indirect
        github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
+       github.com/josharian/intern v1.0.0 // indirect
+       github.com/mailru/easyjson v0.7.7 // indirect
        github.com/pmezard/go-difflib v1.0.0 // indirect
+       github.com/russross/blackfriday/v2 v2.1.0 // indirect
+       github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
        github.com/stretchr/objx v0.1.0 // indirect
-       golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
-       gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+       github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect
+       github.com/swaggo/http-swagger v1.1.2 // indirect
+       github.com/swaggo/swag v1.7.8 // indirect
+       github.com/urfave/cli/v2 v2.3.0 // indirect
+       golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
+       golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
+       golang.org/x/text v0.3.7 // indirect
+       golang.org/x/tools v0.1.7 // indirect
+       gopkg.in/yaml.v2 v2.4.0 // indirect
+       gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
 )
index cf2c90f..f7a6405 100644 (file)
@@ -1,8 +1,38 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/confluentinc/confluent-kafka-go v1.8.2 h1:PBdbvYpyOdFLehj8j+9ba7FL4c4Moxn79gy9cYKxG5E=
 github.com/confluentinc/confluent-kafka-go v1.8.2/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
+github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
+github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
+github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
+github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
+github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
+github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
+github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
@@ -11,18 +41,97 @@ github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxC
 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
 github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
+github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
+github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
+github.com/swaggo/http-swagger v1.1.2 h1:ikcSD+EUOx+2oNZ2N6u8IYa8ScOsAvE7Jh+E1dW6i94=
+github.com/swaggo/http-swagger v1.1.2/go.mod h1:mX5nhypDmoSt4iw2mc5aKXxRFvp1CLLcCiog2B9M+Ro=
+github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
+github.com/swaggo/swag v1.7.8 h1:w249t0l/kc/DKMGlS0fppNJQxKyJ8heNaUWB6nsH3zc=
+github.com/swaggo/swag v1.7.8/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
+github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201207224615-747e23833adb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
index e66a818..0e081a8 100644 (file)
@@ -114,6 +114,33 @@ func TestNew_envFaultyLogLevelConfigContainDefaultValues(t *testing.T) {
        assertions.Contains(logString, "Invalid log level: wrong. Log level will be Info!")
 }
 
+func TestGetJobTypesFromConfiguration_fileOkShouldReturnSliceOfTypeDefinitions(t *testing.T) {
+       assertions := require.New(t)
+       typesDir := CreateTypeConfigFiles(t)
+       t.Cleanup(func() {
+               os.RemoveAll(typesDir)
+       })
+
+       var typeSchemaObj interface{}
+       json.Unmarshal([]byte(typeSchemaFileContent), &typeSchemaObj)
+
+       types, err := GetJobTypesFromConfiguration(typesDir)
+
+       wantedDMaaPType := TypeDefinition{
+               Identity:      "type1",
+               DMaaPTopicURL: "events/unauthenticated.SEC_FAULT_OUTPUT/dmaapmediatorproducer/type1",
+               TypeSchema:    typeSchemaObj,
+       }
+       wantedKafkaType := TypeDefinition{
+               Identity:        "type2",
+               KafkaInputTopic: "TestTopic",
+               TypeSchema:      typeSchemaObj,
+       }
+       wantedTypes := []TypeDefinition{wantedDMaaPType, wantedKafkaType}
+       assertions.EqualValues(wantedTypes, types)
+       assertions.Nil(err)
+}
+
 const typeDefinition = `{"types": [{"id": "type1", "dmaapTopicUrl": "events/unauthenticated.SEC_FAULT_OUTPUT/dmaapmediatorproducer/type1"}, {"id": "type2", "kafkaInputTopic": "TestTopic"}]}`
 const typeSchemaFileContent = `{
        "$schema": "http://json-schema.org/draft-04/schema#",
@@ -126,16 +153,12 @@ const typeSchemaFileContent = `{
        "additionalProperties": false
   }`
 
-func TestGetTypesFromConfiguration_fileOkShouldReturnSliceOfTypeDefinitions(t *testing.T) {
-       assertions := require.New(t)
+func CreateTypeConfigFiles(t *testing.T) string {
        typesDir, err := os.MkdirTemp("", "configs")
        if err != nil {
                t.Errorf("Unable to create temporary directory for types due to: %v", err)
        }
        fname := filepath.Join(typesDir, "type_config.json")
-       t.Cleanup(func() {
-               os.RemoveAll(typesDir)
-       })
        if err = os.WriteFile(fname, []byte(typeDefinition), 0666); err != nil {
                t.Errorf("Unable to create temporary config file for types due to: %v", err)
        }
@@ -147,22 +170,5 @@ func TestGetTypesFromConfiguration_fileOkShouldReturnSliceOfTypeDefinitions(t *t
        if err = os.WriteFile(fname, []byte(typeSchemaFileContent), 0666); err != nil {
                t.Errorf("Unable to create temporary schema file for Kafka type due to: %v", err)
        }
-       var typeSchemaObj interface{}
-       json.Unmarshal([]byte(typeSchemaFileContent), &typeSchemaObj)
-
-       types, err := GetJobTypesFromConfiguration(typesDir)
-
-       wantedDMaaPType := TypeDefinition{
-               Identity:      "type1",
-               DMaaPTopicURL: "events/unauthenticated.SEC_FAULT_OUTPUT/dmaapmediatorproducer/type1",
-               TypeSchema:    typeSchemaObj,
-       }
-       wantedKafkaType := TypeDefinition{
-               Identity:        "type2",
-               KafkaInputTopic: "TestTopic",
-               TypeSchema:      typeSchemaObj,
-       }
-       wantedTypes := []TypeDefinition{wantedDMaaPType, wantedKafkaType}
-       assertions.EqualValues(wantedTypes, types)
-       assertions.Nil(err)
+       return typesDir
 }
index 6a1a70a..2dc700b 100644 (file)
@@ -38,7 +38,7 @@ import (
        "oransc.org/nonrtric/dmaapmediatorproducer/mocks"
 )
 
-func TestJobsManagerGetTypes_shouldReturnSliceOfTypesAndProvideSupportedTypes(t *testing.T) {
+func TestJobsManagerLoadTypesFromConfiguration_shouldReturnSliceOfTypesAndProvideSupportedTypes(t *testing.T) {
        assertions := require.New(t)
 
        managerUnderTest := NewJobsManagerImpl(nil, "", kafkaclient.KafkaFactoryImpl{}, nil)
index 02f3a98..bc4a1da 100644 (file)
@@ -60,6 +60,14 @@ func NewRouter(jm jobs.JobsManager, hcf func(http.ResponseWriter, *http.Request)
        return r
 }
 
+// @Summary      Add info job
+// @Description  Callback for ICS to add an info job
+// @Tags         Data producer (callbacks)
+// @Accept       json
+// @Param        user  body  jobs.JobInfo  true  "Info job data"
+// @Success      200
+// @Failure      400  {string}  Cause  of  error
+// @Router       /info_job [post]
 func (h *ProducerCallbackHandler) addInfoJobHandler(w http.ResponseWriter, r *http.Request) {
        b, readErr := ioutil.ReadAll(r.Body)
        if readErr != nil {
@@ -76,6 +84,12 @@ func (h *ProducerCallbackHandler) addInfoJobHandler(w http.ResponseWriter, r *ht
        }
 }
 
+// @Summary      Delete info job
+// @Description  Callback for ICS to delete an info job
+// @Tags         Data producer (callbacks)
+// @Param        infoJobId  path  string  true  "Info job ID"
+// @Success      200
+// @Router       /info_job/{infoJobId} [delete]
 func (h *ProducerCallbackHandler) deleteInfoJobHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        id, ok := vars[jobIdToken]
@@ -87,6 +101,13 @@ func (h *ProducerCallbackHandler) deleteInfoJobHandler(w http.ResponseWriter, r
        h.jobsManager.DeleteJobFromRESTCall(id)
 }
 
+// @Summary      Set log level
+// @Description  Set the log level of the producer.
+// @Tags         Admin
+// @Param        level  query  string  false  "string enums"  Enums(Error, Warn, Info, Debug)
+// @Success      200
+// @Failure      400  {string}  Cause  of  error
+// @Router       /admin/log [put]
 func (h *ProducerCallbackHandler) setLogLevel(w http.ResponseWriter, r *http.Request) {
        query := r.URL.Query()
        logLevelStr := query.Get(logLevelToken)
index 819ffa9..ab28c6b 100644 (file)
@@ -26,12 +26,16 @@ import (
        "net/http"
        "time"
 
+       "github.com/gorilla/mux"
        log "github.com/sirupsen/logrus"
+       _ "oransc.org/nonrtric/dmaapmediatorproducer/docs"
        "oransc.org/nonrtric/dmaapmediatorproducer/internal/config"
        "oransc.org/nonrtric/dmaapmediatorproducer/internal/jobs"
        "oransc.org/nonrtric/dmaapmediatorproducer/internal/kafkaclient"
        "oransc.org/nonrtric/dmaapmediatorproducer/internal/restclient"
        "oransc.org/nonrtric/dmaapmediatorproducer/internal/server"
+
+       httpSwagger "github.com/swaggo/http-swagger"
 )
 
 var configuration *config.Config
@@ -41,6 +45,12 @@ func init() {
        configuration = config.New()
 }
 
+// @title DMaaP Mediator Producer
+// @version 1.1.0
+
+// @license.name  Apache 2.0
+// @license.url   http://www.apache.org/licenses/LICENSE-2.0.html
+
 func main() {
        log.SetLevel(configuration.LogLevel)
        log.Debug("Initializing DMaaP Mediator Producer")
@@ -87,20 +97,20 @@ func validateConfiguration(configuration *config.Config) error {
        }
        return nil
 }
-func registerTypesAndProducer(jobTypesHandler jobs.JobTypesManager, infoCoordinatorAddress string, callbackAddress string, client restclient.HTTPClient) error {
+func registerTypesAndProducer(jobTypesManager jobs.JobTypesManager, infoCoordinatorAddress string, callbackAddress string, client restclient.HTTPClient) error {
        registrator := config.NewRegistratorImpl(infoCoordinatorAddress, client)
        configTypes, err := config.GetJobTypesFromConfiguration("configs")
        if err != nil {
                return fmt.Errorf("unable to register all types due to: %v", err)
        }
-       regErr := registrator.RegisterTypes(jobTypesHandler.LoadTypesFromConfiguration(configTypes))
+       regErr := registrator.RegisterTypes(jobTypesManager.LoadTypesFromConfiguration(configTypes))
        if regErr != nil {
                return fmt.Errorf("unable to register all types due to: %v", regErr)
        }
 
        producer := config.ProducerRegistrationInfo{
                InfoProducerSupervisionCallbackUrl: callbackAddress + server.HealthCheckPath,
-               SupportedInfoTypes:                 jobTypesHandler.GetSupportedTypes(),
+               SupportedInfoTypes:                 jobTypesManager.GetSupportedTypes(),
                InfoJobCallbackUrl:                 callbackAddress + server.AddJobPath,
        }
        if err := registrator.RegisterProducer("DMaaP_Mediator_Producer", &producer); err != nil {
@@ -112,6 +122,7 @@ func registerTypesAndProducer(jobTypesHandler jobs.JobTypesManager, infoCoordina
 func startCallbackServer(jobsManager jobs.JobsManager, callbackAddress string) {
        log.Debugf("Starting callback server at port %v", configuration.InfoProducerPort)
        r := server.NewRouter(jobsManager, statusHandler)
+       addSwaggerHandler(r)
        if restclient.IsUrlSecure(callbackAddress) {
                log.Fatalf("Server stopped: %v", http.ListenAndServeTLS(fmt.Sprintf(":%v", configuration.InfoProducerPort), configuration.ProducerCertPath, configuration.ProducerKeyPath, r))
        } else {
@@ -119,6 +130,11 @@ func startCallbackServer(jobsManager jobs.JobsManager, callbackAddress string) {
        }
 }
 
+// @Summary Get status
+// @Description Get the status of the producer. Will show if the producer has registered in ICS.
+// @Tags Data producer (callbacks)
+// @Success 200
+// @Router /health_check [get]
 func statusHandler(w http.ResponseWriter, r *http.Request) {
        registeredStatus := "not registered"
        if registered {
@@ -127,6 +143,15 @@ func statusHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, `{"status": "%v"}`, registeredStatus)
 }
 
+// @Summary Get Swagger Documentation
+// @Description Get the Swagger API documentation for the producer.
+// @Tags Admin
+// @Success 200
+// @Router /swagger [get]
+func addSwaggerHandler(r *mux.Router) {
+       r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)
+}
+
 func keepProducerAlive() {
        forever := make(chan int)
        <-forever
diff --git a/dmaap-mediator-producer/main_test.go b/dmaap-mediator-producer/main_test.go
new file mode 100644 (file)
index 0000000..3653e9e
--- /dev/null
@@ -0,0 +1,201 @@
+// -
+//   ========================LICENSE_START=================================
+//   O-RAN-SC
+//   %%
+//   Copyright (C) 2022: Nordix Foundation
+//   %%
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//        http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+//   ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+       "bytes"
+       "io/ioutil"
+       "net/http"
+       "os/exec"
+       "sync"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/require"
+       "oransc.org/nonrtric/dmaapmediatorproducer/internal/config"
+       "oransc.org/nonrtric/dmaapmediatorproducer/internal/jobs"
+       "oransc.org/nonrtric/dmaapmediatorproducer/internal/kafkaclient"
+)
+
+// This is not a real test, just a way to get the Swagger documentation generated automatically.
+// Hence there are no assertions in this test.
+func TestGenerateSwaggerDocs(t *testing.T) {
+       cmd := exec.Command("./generate_swagger_docs.sh")
+
+       cmd.Run()
+}
+
+func TestValidateConfiguration(t *testing.T) {
+       assertions := require.New(t)
+
+       validConfig := config.Config{
+               InfoProducerHost:      "host",
+               DMaaPMRAddress:        "address",
+               KafkaBootstrapServers: "servers",
+               ProducerCertPath:      "path",
+               ProducerKeyPath:       "path",
+       }
+       assertions.Nil(validateConfiguration(&validConfig))
+
+       missingProducerHost := config.Config{
+               DMaaPMRAddress:        "address",
+               KafkaBootstrapServers: "servers",
+               ProducerCertPath:      "path",
+               ProducerKeyPath:       "path",
+       }
+       assertions.Contains(validateConfiguration(&missingProducerHost).Error(), "INFO_PRODUCER_HOST")
+
+       missingCert := config.Config{
+               InfoProducerHost:      "host",
+               DMaaPMRAddress:        "address",
+               KafkaBootstrapServers: "servers",
+               ProducerKeyPath:       "path",
+       }
+       assertions.Contains(validateConfiguration(&missingCert).Error(), "PRODUCER_CERT")
+
+       missingCertKey := config.Config{
+               InfoProducerHost:      "host",
+               DMaaPMRAddress:        "address",
+               KafkaBootstrapServers: "servers",
+               ProducerCertPath:      "path",
+       }
+       assertions.Contains(validateConfiguration(&missingCertKey).Error(), "PRODUCER_KEY")
+
+       missingMRAddress := config.Config{
+               InfoProducerHost:      "host",
+               KafkaBootstrapServers: "servers",
+               ProducerCertPath:      "path",
+               ProducerKeyPath:       "path",
+       }
+       assertions.Nil(validateConfiguration(&missingMRAddress))
+
+       missingKafkaServers := config.Config{
+               InfoProducerHost: "host",
+               DMaaPMRAddress:   "address",
+               ProducerCertPath: "path",
+               ProducerKeyPath:  "path",
+       }
+       assertions.Nil(validateConfiguration(&missingKafkaServers))
+
+       missingMRAddressdAndKafkaServers := config.Config{
+               InfoProducerHost: "host",
+               ProducerCertPath: "path",
+               ProducerKeyPath:  "path",
+       }
+       assertions.Contains(validateConfiguration(&missingMRAddressdAndKafkaServers).Error(), "DMAAP_MR_ADDR")
+       assertions.Contains(validateConfiguration(&missingMRAddressdAndKafkaServers).Error(), "KAFKA_BOOTSRAP_SERVERS")
+}
+
+func TestRegisterTypesAndProducer(t *testing.T) {
+       assertions := require.New(t)
+
+       wg := sync.WaitGroup{}
+       clientMock := NewTestClient(func(req *http.Request) *http.Response {
+               if req.URL.String() == configuration.InfoCoordinatorAddress+"/data-producer/v1/info-types/STD_Fault_Messages" {
+                       assertions.Equal(req.Method, "PUT")
+                       body := getBodyAsString(req, t)
+                       assertions.Contains(body, "info_job_data_schema")
+                       assertions.Equal("application/json", req.Header.Get("Content-Type"))
+                       wg.Done()
+                       return &http.Response{
+                               StatusCode: 200,
+                               Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
+                               Header:     make(http.Header), // Must be set to non-nil value or it panics
+                       }
+               } else if req.URL.String() == configuration.InfoCoordinatorAddress+"/data-producer/v1/info-types/Kafka_TestTopic" {
+                       assertions.Equal(req.Method, "PUT")
+                       body := getBodyAsString(req, t)
+                       assertions.Contains(body, "info_job_data_schema")
+                       assertions.Equal("application/json", req.Header.Get("Content-Type"))
+                       wg.Done()
+                       return &http.Response{
+                               StatusCode: 200,
+                               Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
+                               Header:     make(http.Header), // Must be set to non-nil value or it panics
+                       }
+               } else if req.URL.String() == configuration.InfoCoordinatorAddress+"/data-producer/v1/info-producers/DMaaP_Mediator_Producer" {
+                       assertions.Equal(req.Method, "PUT")
+                       body := getBodyAsString(req, t)
+                       assertions.Contains(body, "callbackAddress/health_check")
+                       assertions.Contains(body, "callbackAddress/info_job")
+                       assertions.Contains(body, "Kafka_TestTopic")
+                       assertions.Contains(body, "STD_Fault_Messages")
+                       assertions.Equal("application/json", req.Header.Get("Content-Type"))
+                       wg.Done()
+                       return &http.Response{
+                               StatusCode: 200,
+                               Body:       ioutil.NopCloser(bytes.NewBufferString(`OK`)),
+                               Header:     make(http.Header), // Must be set to non-nil value or it panics
+                       }
+               }
+               t.Error("Wrong call to client: ", req)
+               t.Fail()
+               return nil
+       })
+       jobsManager := jobs.NewJobsManagerImpl(clientMock, configuration.DMaaPMRAddress, kafkaclient.KafkaFactoryImpl{}, nil)
+
+       wg.Add(3)
+       err := registerTypesAndProducer(jobsManager, configuration.InfoCoordinatorAddress, "callbackAddress", clientMock)
+
+       assertions.Nil(err)
+
+       if waitTimeout(&wg, 2*time.Second) {
+               t.Error("Not all calls to server were made")
+               t.Fail()
+       }
+}
+
+type RoundTripFunc func(req *http.Request) *http.Response
+
+func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
+       return f(req), nil
+}
+
+//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
+func NewTestClient(fn RoundTripFunc) *http.Client {
+       return &http.Client{
+               Transport: RoundTripFunc(fn),
+       }
+}
+
+func getBodyAsString(req *http.Request, t *testing.T) string {
+       buf := new(bytes.Buffer)
+       if _, err := buf.ReadFrom(req.Body); err != nil {
+               t.Fail()
+       }
+       return buf.String()
+}
+
+// waitTimeout waits for the waitgroup for the specified max timeout.
+// Returns true if waiting timed out.
+func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
+       c := make(chan struct{})
+       go func() {
+               defer close(c)
+               wg.Wait()
+       }()
+       select {
+       case <-c:
+               return false // completed normally
+       case <-time.After(timeout):
+               return true // timed out
+       }
+}
index 8ab5b45..c0baa17 100644 (file)
@@ -46,7 +46,7 @@ The API is also described in Swagger-JSON and YAML:
 DMaaP Adaptor
 =============
 
-The DMaaP Adaptor provides support for push delivery of any data received from DMaap or Kafka.
+The DMaaP Adaptor provides support for push delivery of any data received from DMaaP or Kafka.
 
 See `DMaaP Adaptor API <./dmaap-adaptor-api.html>`_ for full details of the API.
 
@@ -59,6 +59,22 @@ The API is also described in Swagger-JSON and YAML:
 
    "DMaaP Adaptor API", ":download:`link <../dmaap-adaptor-java/api/api.json>`", ":download:`link <../dmaap-adaptor-java/api/api.yaml>`"
 
+DMaaP Mediator Producer
+=======================
+
+The DMaaP Mediator Producer provides support for push delivery of any data received from DMaaP or Kafka.
+
+See `DMaaP Mediator Producer API <./dmaap-mediator-producer-api.html>`_ for full details of the API.
+
+The API is also described in Swagger-JSON and YAML:
+
+
+.. csv-table::
+   :header: "API name", "|swagger-icon|", "|yaml-icon|"
+   :widths: 10,5, 5
+
+   "DMaaP Mediator Producer API", ":download:`link <../dmaap-mediator-producer/docs/swagger.json>`", ":download:`link <../dmaap-mediator-producer/docs/swagger.yaml>`"
+
 Non-RT-RIC App Catalogue (Initial)
 ==================================
 
index c5e504d..d4cdb81 100644 (file)
@@ -11,6 +11,7 @@ linkcheck_ignore = [
     './rac-api.html', #Generated file that doesn't exist at link check.
     './ics-api.html', #Generated file that doesn't exist at link check.
     './dmaap-adaptor-api.html' #Generated file that doesn't exist at link check.
+    './dmaap-mediator-producer-api.html' #Generated file that doesn't exist at link check.
 ]
 
 extensions = ['sphinxcontrib.redoc', 'sphinx.ext.intersphinx',]
@@ -32,6 +33,11 @@ redoc = [
                 'name': 'DMaaP Adaptor API',
                 'page': 'dmaap-adaptor-api',
                 'spec': '../dmaap-adaptor-java/api/api.json',
+            },
+            {
+                'name': 'DMaaP Mediator Producer API',
+                'page': 'dmaap-mediator-producer-api',
+                'spec': '../dmaap-mediator-producer/docs/swagger.json',
                 'embed': True,
             }
         ]
index 0d950cd..a3c3422 100644 (file)
@@ -7,12 +7,12 @@ Developer Guide
 
 This document provides a quickstart for developers of the Non-RT RIC parts.
 
-Additional developer guides are available on the `O-RAN SC NONRTRIC Developer wiki <https://wiki.o-ran-sc.org/display/RICNR/Release+E>`_
+Additional developer guides are available on the `O-RAN SC NONRTRIC Developer wiki <https://wiki.o-ran-sc.org/display/RICNR/Release+E>`_.
 
 A1 Policy Management Service & SDNC/A1 Controller & A1 Adapter
 --------------------------------------------------------------
 
-The A1 Policy Management Service is implemented in ONAP. For documentation see `ONAP CCSDK documentation <https://docs.onap.org/projects/onap-ccsdk-oran/en/latest/index.html>`_
+The A1 Policy Management Service is implemented in ONAP. For documentation see `ONAP CCSDK documentation <https://docs.onap.org/projects/onap-ccsdk-oran/en/latest/index.html>`_.
 and `wiki <https://wiki.onap.org/pages/viewpage.action?pageId=84672221>`_.
 
 Information Coordinator Service
@@ -65,36 +65,42 @@ See the README.md file in the *r-app-catalogue* directory in the Gerrit repo for
 
 DMaaP Adaptor Service
 ---------------------
-This is run in the same way as the Information Coordinator Service
+
+This Java implementation is run in the same way as the Information Coordinator Service.
 
 The following properties in the application.yaml file have to be modified:
 * server.ssl.key-store=./config/keystore.jks
 * app.webclient.trust-store=./config/truststore.jks
 * app.configuration-filepath=./src/test/resources/test_application_configuration.json
 
+DMaaP Mediator Producer
+-----------------------
+
+To build and run this Go implementation, see the README.md file under the folder "dmaap-mediator-producer" in the "nonrtric" repo.
+
 O-DU & O-RU fronthaul recovery
 ------------------------------
 
-See the page in Wiki: `O-RU Fronthaul Recovery usecase <https://wiki.o-ran-sc.org/display/RICNR/O-RU+Fronthaul+Recovery+usecase>`_
+See the page in Wiki: `O-RU Fronthaul Recovery usecase <https://wiki.o-ran-sc.org/display/RICNR/O-RU+Fronthaul+Recovery+usecase>`_.
 
 O-DU Slicing use cases
 ----------------------
 
-See the page in Wiki: `O-DU Slice Assurance usecase <https://wiki.o-ran-sc.org/display/RICNR/O-DU+Slice+Assurance+usecase>`_
+See the page in Wiki: `O-DU Slice Assurance usecase <https://wiki.o-ran-sc.org/display/RICNR/O-DU+Slice+Assurance+usecase>`_.
 
 Helm Manager
 ------------
 
-See the page in Wiki: `Release E <https://wiki.o-ran-sc.org/display/RICNR/Release+E>`_
+See the page in Wiki: `Release E <https://wiki.o-ran-sc.org/display/RICNR/Release+E>`_.
 
 Kubernetes deployment
 =====================
 
-Non-RT RIC can be also deployed in a Kubernetes cluster, `it/dep repository <https://gerrit.o-ran-sc.org/r/admin/repos/it/dep>`_
+Non-RT RIC can be also deployed in a Kubernetes cluster, `it/dep repository <https://gerrit.o-ran-sc.org/r/admin/repos/it/dep>`_.
 hosts deployment and integration artifacts. Instructions and helm charts to deploy the Non-RT-RIC functions in the
 OSC NONRTRIC integrated test environment can be found in the *./nonrtric* directory.
 
-For more information on installation of NonRT-RIC in Kubernetes, see `Deploy NONRTRIC in Kubernetes <https://wiki.o-ran-sc.org/display/RICNR/Deploy+NONRTRIC+in+Kubernetes>`_
+For more information on installation of NonRT-RIC in Kubernetes, see `Deploy NONRTRIC in Kubernetes <https://wiki.o-ran-sc.org/display/RICNR/Deploy+NONRTRIC+in+Kubernetes>`_.
 
 For more information see `Integration and Testing documentation on the O-RAN-SC wiki <https://docs.o-ran-sc.org/projects/o-ran-sc-it-dep/en/latest/index.html>`_.
 
index 17e3492..1837152 100644 (file)
@@ -25,8 +25,8 @@ command to start the components:
            -f policy-service/docker-compose.yaml
            -f ics/docker-compose.yaml
 
-The example above is just an example to start some of the components. 
-For more information on running and configuring the functions can be found in the README file in the "`docker-compose <https://gerrit.o-ran-sc.org/r/gitweb?p=nonrtric.git;a=tree;f=docker-compose>`__" folder, and on the `wiki page <https://wiki.o-ran-sc.org/display/RICNR/Release+E+-+Run>`_
+The example above is just an example to start some of the components.
+For more information on running and configuring the functions can be found in the README file in the "`docker-compose <https://gerrit.o-ran-sc.org/r/gitweb?p=nonrtric.git;a=tree;f=docker-compose>`__" folder, and on the `wiki page <https://wiki.o-ran-sc.org/display/RICNR/Release+E+-+Run+in+Docker>`_
 
 Install with Helm
 +++++++++++++++++
index 8126776..19194f4 100644 (file)
@@ -8,13 +8,13 @@
 Summary
 -------
 
-The Non-RealTime RIC (RAN Intelligent Controller) is an Orchestration and Automation function described by the O-RAN Alliance for non-real-time intelligent management of RAN (Radio Access Network) functions. 
+The Non-RealTime RIC (RAN Intelligent Controller) is an Orchestration and Automation function described by the O-RAN Alliance for non-real-time intelligent management of RAN (Radio Access Network) functions.
 
-The primary goal of the Non-RealTime RIC is to support non-real-time radio resource management, higher layer procedure optimization, policy optimization in RAN, and providing guidance, parameters, policies and AI/ML models to support the operation of near-RealTime RIC functions in the RAN to achieve higher-level non-real-time objectives. 
+The primary goal of the Non-RealTime RIC is to support non-real-time radio resource management, higher layer procedure optimization, policy optimization in RAN, and providing guidance, parameters, policies and AI/ML models to support the operation of near-RealTime RIC functions in the RAN to achieve higher-level non-real-time objectives.
 
-Non-RealTime RIC functions include service and policy management, RAN analytics and model-training for the near-RealTime RICs. 
+Non-RealTime RIC functions include service and policy management, RAN analytics and model-training for the near-RealTime RICs.
 The Non-RealTime RIC platform hosts and coordinates rApps (Non-RT RIC applications) to perform Non-RealTime RIC tasks.
-The Non-RealTime RIC also hosts the new R1 interface (between rApps and SMO/Non-RealTime-RIC services)
+The Non-RealTime RIC also hosts the new R1 interface (between rApps and SMO/Non-RealTime-RIC services).
 
 The O-RAN-SC (OSC) NONRTRIC project provides concepts, architecture and reference implementations as defined and described by the `O-RAN Alliance <https://www.o-ran.org>`_ architecture.
 The OSC NONRTRIC implementation communicates with near-RealTime RIC elements in the RAN via the A1 interface. Using the A1 interface the NONRTRIC will facilitate the provision of policies for individual UEs or groups of UEs; monitor and provide basic feedback on policy state from near-RealTime RICs; provide enrichment information as required by near-RealTime RICs; and facilitate ML model training, distribution and inference in cooperation with the near-RealTime RICs.
@@ -49,7 +49,7 @@ The source code for "E" Release is in the `NONRTRIC <https://gerrit.o-ran-sc.org
 Non-RT-RIC Control Panel / NONRTRIC Dashboard
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Graphical user interface
+Graphical user interface.
 
 * View and Manage A1 policies in the RAN (near-RT-RICs)
 * Graphical A1 policy creation/editing is model-driven, based on policy type's JSON schema
@@ -64,7 +64,7 @@ Implementation:
 
 Please refer the developer guide and the `Wiki <https://wiki.o-ran-sc.org/display/RICNR/>`_ to set up in your local environment.
 
-More details available at the `NONRTRIC-Portal documentation site <https://docs.o-ran-sc.org/projects/o-ran-sc-portal-nonrtric-controlpanel>`_
+More details available at the `NONRTRIC-Portal documentation site <https://docs.o-ran-sc.org/projects/o-ran-sc-portal-nonrtric-controlpanel>`_.
 
 Information Coordination Service
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -80,140 +80,138 @@ Coordinate/Register A1-EI Types, Producers, Consumers, and Jobs (A1 Enrichment I
   + Information Consumers
   + Information Jobs
 
-* Information Query API (e.g. per producer, per consumer, per types)
-* Query status of Information jobs
-* After Information-type/Producer/Consumer/Job is successfully registered delivery/flow can happen directly between Information Producers and Information Consumers
+* Information Query API (e.g. per producer, per consumer, per types).
+* Query status of Information jobs.
+* After Information-type/Producer/Consumer/Job is successfully registered delivery/flow can happen directly between Information Producers and Information Consumers.
 * The Information Coordinator Service natively supports the O-RAN A1 Enrichment Information (A1-EI) interface, supporting coordination A1-EI Jobs where information (A1-EI)flow from the SMO/Non-RT-RIC/rApps to near-RT-RICs over the A1 interface.
 
 Implementation:
 
-* Implemented as a Java Spring Boot application
+* Implemented as a Java Spring Boot application.
 
 A1 Policy Management Service (from ONAP CCSDK)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 A1 Controller Service above A1 Controller/Adaptor that provides:
 
-* Unified REST & DMaaP NBI APIs for managing A1 Policies in all near-RT-RICs
+* Unified REST & DMaaP NBI APIs for managing A1 Policies in all near-RT-RICs.
 
-  + Query A1 Policy Types in near-RT-RICs
-  + Create/Query/Update/Delete A1 Policy Instances in near-RT-RICs
-  + Query Status for A1 Policy Instances
+  + Query A1 Policy Types in near-RT-RICs.
+  + Create/Query/Update/Delete A1 Policy Instances in near-RT-RICs.
+  + Query Status for A1 Policy Instances.
 
-* Maintains (persistent) cache of RAN's A1 Policy information
+* Maintains (persistent) cache of RAN's A1 Policy information.
 
-  * Support RAN-wide view of A1 Policy information
-  * Streamline A1 traffic
-  * Enable (optional) re-synchronization after inconsistencies / near-RT-RIC restarts
-  * Supports a large number of near-RT-RICs (& multi-version support)
+  * Support RAN-wide view of A1 Policy information.
+  * Streamline A1 traffic.
+  * Enable (optional) re-synchronization after inconsistencies / near-RT-RIC restarts.
+  * Supports a large number of near-RT-RICs (& multi-version support).
 
-* Converged ONAP & O-RAN-SC A1 Adapter/Controller functions in ONAP SDNC/CCSDK (Optionally deploy without A1 Adaptor to connect direct to near-RT-RICs)
-* Support for different Southbound connectors per near-RT-RIC - e.g. different A1 versions, different near-RT-RIC version, different A1 adapter/controllers supports different or proprietary A1 controllers/EMSs
+* Converged ONAP & O-RAN-SC A1 Adapter/Controller functions in ONAP SDNC/CCSDK (Optionally deploy without A1 Adaptor to connect direct to near-RT-RICs).
+* Support for different Southbound connectors per near-RT-RIC - e.g. different A1 versions, different near-RT-RIC version, different A1 adapter/controllers supports different or proprietary A1 controllers/EMSs.
 
-See also: `A1 Policy Management Service in ONAP <https://wiki.onap.org/pages/viewpage.action?pageId=84672221>`_ 
+See also: `A1 Policy Management Service in ONAP <https://wiki.onap.org/pages/viewpage.action?pageId=84672221>`_ .
 
 Implementation:
 
-* Implemented as a Java Spring Boot application
+* Implemented as a Java Spring Boot application.
 
 A1/SDNC Controller & A1 Adapter (Controller plugin)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Mediation point for A1 interface termination in SMO/NONRTRIC
+Mediation point for A1 interface termination in SMO/NONRTRIC.
 
-* Implemented as CCSDK OSGI Feature/Bundles
-* A1 REST southbound
-* RESTCONF Northbound
-* NETCONF YANG > RESTCONF adapter
-* SLI Mapping logic supported
-* Can be included in an any controller based on ONAP CCSDK
+* Implemented as CCSDK OSGI Feature/Bundles.
+* A1 REST southbound.
+* RESTCONF Northbound.
+* NETCONF YANG > RESTCONF adapter.
+* SLI Mapping logic supported.
+* Can be included in an any controller based on ONAP CCSDK.
 
-See also: `A1 Adapter/Controller Functions in ONAP <https://wiki.onap.org/pages/viewpage.action?pageId=84672221>`_ 
+See also: `A1 Adapter/Controller Functions in ONAP <https://wiki.onap.org/pages/viewpage.action?pageId=84672221>`_ .
 
 A1 Interface / Near-RT-RIC Simulator
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Stateful A1 test stub.
 
-* Used to create multiple stateful A1 providers (simulated near-rt-rics)
-* Supports A1-Policy and A1-Enrichment Information
-* Swagger-based northbound interface, so easy to change the A1 profile exposed (e.g. A1 version, A1 Policy Types, A1-E1 consumers, etc)
-* All A1-AP versions supported
+* Used to create multiple stateful A1 providers (simulated near-rt-rics).
+* Supports A1-Policy and A1-Enrichment Information.
+* Swagger-based northbound interface, so easy to change the A1 profile exposed (e.g. A1 version, A1 Policy Types, A1-E1 consumers, etc).
+* All A1-AP versions supported.
 
 Implementation:
 
-* Implemented as a Python application
-* Repo: *sim/a1-interface*
+* Implemented as a Python application.
+* Repo: *sim/a1-interface*.
 
 More details available at the `A1 Simulator documentation site <https://docs.o-ran-sc.org/projects/o-ran-sc-sim-a1-interface>`_
 
 Non-RT-RIC (Spring Cloud) Service Gateway
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Support Apps to use A1 Services 
+Support Apps to use A1 Services.
 
-* `Spring Cloud Gateway <https://cloud.spring.io/spring-cloud-gateway>`_ provides the library to build a basic API gateway
-* Exposes A1 Policy Management Service & Information Coordinator Service. 
+* `Spring Cloud Gateway <https://cloud.spring.io/spring-cloud-gateway>`_ provides the library to build a basic API gateway.
+* Exposes A1 Policy Management Service & Information Coordinator Service.
 * Additional predicates can be added in code or preferably in the Gateway yaml configuration.
 
 Implementation:
 
-* Implemented as a Java Spring Cloud application
-* Repo: *portal/nonrtric-controlpanel*
+* Implemented as a Java Spring Cloud application.
+* Repo: *portal/nonrtric-controlpanel*.
 
 
 Non-RT-RIC (Kong) Service Exposure Prototyping
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Support Apps to use NONRTRIC, SMO and other App interfaces
-A building block for coming releases as the R1 Interface concept matures 
+Support Apps to use NONRTRIC, SMO and other App interfaces.
+A building block for coming releases as the R1 Interface concept matures .
 
-* Support dynamic registration and exposure of service interfaces to Non-RT-RIC applications (& NONRTRIC Control panel)
-* Extends a static gateway function specifically for NONRTRIC Control panel (described above)
-* Initial version based on `Kong API Gateway <https://docs.konghq.com/gateway-oss>`_ function
-* Initial exposure candidates include A1 (NONRTRIC) services & O1 (OAM/SMO) services
+* Support dynamic registration and exposure of service interfaces to Non-RT-RIC applications (& NONRTRIC Control panel).
+* Extends a static gateway function specifically for NONRTRIC Control panel (described above).
+* Initial version based on `Kong API Gateway <https://docs.konghq.com/gateway-oss>`_ function.
+* Initial exposure candidates include A1 (NONRTRIC) services & O1 (OAM/SMO) services.
 
-NONRTRIC Kubernetes deployment - including Kong configurations can be found in the OSC `it/dep <https://gerrit.o-ran-sc.org/r/gitweb?p=it/dep.git;a=tree;f=nonrtric/helm/nonrtric>`_ Gerrit repo. 
+NONRTRIC Kubernetes deployment - including Kong configurations can be found in the OSC `it/dep <https://gerrit.o-ran-sc.org/r/gitweb?p=it/dep.git;a=tree;f=nonrtric/helm/nonrtric>`_ Gerrit repo.
 
 DMaaP/Kafka Information Producer Adapters
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Configurable mediators to take information from DMaaP (& Kafka) and present it as a coordinated Information Producer 
-
-These mediators/adapters are generic information producers, which registers themselves as an information producers of defined information types (in Information Coordination Service).
-The information types are defined in a configuration file. 
-Information jobs defined using Information Coordination Service (ICS) then allow information consumers to retrieve data from DMaaP MR or Kafka topics (accessing the ICS API).
+Configurable mediators to take information from DMaaP and Kafka and present it as a coordinated Information Producer.
 
-Two alternative implementations to allow Information Consumers to consume DMaaP or Kafka events as coordinated Information Jobs.
+These mediators/adapters are generic information producers, which register themselves as information producers of defined information types in Information Coordination Service (ICS).
+The information types are defined in a configuration file.
+Information jobs defined using ICS then allow information consumers to retrieve data from DMaaP MR or Kafka topics (accessing the ICS API).
 
-Implementations:
+There are two alternative implementations to allow Information Consumers to consume DMaaP or Kafka events as coordinated Information Jobs.
 
-1. A version implemented in Java (Spring) - Supporting DMaaP and Kafka mediation
-2. A version implemented in Go - Supporting DMaaP mediation 
+1. A version implemented in Java Spring (DMaaP Adaptor Service).
+2. A version implemented in Go (DMaaP Mediator Producer).
 
 Initial Non-RT-RIC App Catalogue
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Register for Non-RT-RIC Apps.
 
-* Non-RT-RIC Apps can be registered / queried
-* Limited functionality/integration for now
-* *More work required in coming releases as the rApp concept matures*
+* Non-RT-RIC Apps can be registered / queried.
+* Limited functionality/integration for now.
+* *More work required in coming releases as the rApp concept matures*.
 
 Initial K8S Helm Chart LCM Manager
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Onboard, start, stop, and modify Non-RT-RIC App µServices as Helm Charts
-*A building block for coming releases as the R-APP concept matures*
+Onboard, start, stop, and modify Non-RT-RIC App µServices as Helm Charts.
+*A building block for coming releases as the R-APP concept matures*.
 
-* Interfaces that accepts Non-RT-RIC App µServices Helm Charts
-* Support basic LCM operations
-* Onboard, Start, Stop, Modify, Monitor
-* Initial version co-developed with v. similar functions in ONAP
-* *Limited functionality/integration for now*
+* Interfaces that accepts Non-RT-RIC App µServices Helm Charts.
+* Support basic LCM operations.
+* Onboard, Start, Stop, Modify, Monitor.
+* Initial version co-developed with v. similar functions in ONAP.
+* *Limited functionality/integration for now*.
 
 Test Framework
 ~~~~~~~~~~~~~~
 
-A full test environment with extensive test cases/scripts can be found in the ``test`` directory in the *nonrtric* source code
+A full test environment with extensive test cases/scripts can be found in the ``test`` directory in the *nonrtric* source code.
 
 Use Cases
 ~~~~~~~~~
@@ -221,9 +219,9 @@ Use Cases
 "Helloworld" O-RU Fronthaul Recovery use case
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-A very simplified closed-loop rApp use case to re-establish front-haul connections between O-DUs and O-RUs if they fail. Not intended to to be 'real-world'
+A very simplified closed-loop rApp use case to re-establish front-haul connections between O-DUs and O-RUs if they fail. Not intended to to be 'real-world'.
 
 "Helloworld" O-DU Slice Assurance use case
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-A very simplified closed-loop rApp use case to re-prioritize a RAN slice's radio resource allocation priority if sufficient throughput cannot be maintained. Not intended to to be 'real-world'
+A very simplified closed-loop rApp use case to re-prioritize a RAN slice's radio resource allocation priority if sufficient throughput cannot be maintained. Not intended to to be 'real-world'.