Skip to content
This repository has been archived by the owner on May 14, 2021. It is now read-only.

MINIFI-444 C2 Data Model and REST API #118

Closed

Conversation

kevdoran
Copy link
Contributor

@kevdoran kevdoran commented Mar 16, 2018

Adds the foundation of a data model and REST API that would support
discussed features such as a MiNiFi Agent C2 Protocol and
MiNiFi Flow Designer.

Thank you for submitting a contribution to Apache NiFi - MiNiFi.

In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:

For all changes:

  • Is there a JIRA ticket associated with this PR? Is it referenced
    in the commit message?

  • Does your PR title start with MINIFI-XXXX where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.

  • Has your PR been rebased against the latest commit within the target branch (typically master)?

  • Is your initial contribution a single, squashed commit?

For code changes:

  • Have you ensured that the full suite of tests is executed via mvn -Pcontrib-check clean install at the root nifi-minifi folder?
  • Have you written or updated unit tests to verify your changes?
  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?
  • If applicable, have you updated the LICENSE file, including the main LICENSE file under minifi-assembly?
  • If applicable, have you updated the NOTICE file, including the main NOTICE file found under minifi-assembly?

For documentation related changes:

  • Have you ensured that format looks appropriate for the output in which it is rendered?

Note:

Please ensure that once the PR is submitted, you check travis-ci for build issues and submit an update to your PR as soon as possible.

The foundation of a data model and REST API that would support
discussed features such as a MiNiFi Agent C2 Protocol and
MiNiFi Flow Designer.
Copy link

@phrocker phrocker left a comment

Choose a reason for hiding this comment

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

Thanks for putting this up. Took a cursory review. Will take a look later/this weekend if I have a chance.


private String identifier;
// TODO, do we also need identity. e.g., cert DN
private String agentClass;

Choose a reason for hiding this comment

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

Any reason why this is not more strongly typed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

[Some background to those who are new to the terminology that's starting to formulate around these features: In this context, class != OO type. Rather, it's intended as a category/purpose label for a MiNiFi agent that could be used for a variety of purposes from a C2 server: e.g., seeing summary views of a class, showing all agents of a particular class, scheduling a bulk operation (such as deploying flow update) to all agents that match a particular class.]

In the data model, I do have an AgentClass type that could be used here, but I think that will really just be metadata that one can associate with a class in the C2 server. We'll have to enforce agent class name uniqueness, so that in contexts where the full agent class metadata is not needed, just the class name can be used.

private String identifier;
// TODO, do we also need identity. e.g., cert DN
private String agentClass;
private Collection<String> tags;

Choose a reason for hiding this comment

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

What are tags?

Copy link
Contributor Author

@kevdoran kevdoran Mar 19, 2018

Choose a reason for hiding this comment

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

I was thinking it would be nice for an operator to be able to tag agents with arbitrary labels that they can then use as filters in future queries. I'm not sure those tags would be just on the C2 server in the agents it is tracking, or if they could also be set on agents when they are provisioned and deployed, similar to the agent class labels.

Thanks for calling this out. Now that I think about it, I'd like to drop this field for now so that we can properly discuss (on the mailing list, JIRA, etc) if this is valuable and if so how to implement it.

  • Remove tags from AgentInfo

@ApiModel
public class AgentManifest {

private String identifier;

Choose a reason for hiding this comment

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

is this referential to the identifiers above?

Copy link
Contributor Author

@kevdoran kevdoran Mar 19, 2018

Choose a reason for hiding this comment

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

No, this is a new identifier that will only be used by the C2 server. It assumes the agent manifests are going to be persistable and reusable. It probably would not be present when an agent manifest is being uploaded to a C2 server (e.g., in a C2 heartbeat from a MiNiFi agent or as the output of a MiNiFi agent build that some build script uploads), but when the C2 server persists it, it will serve as a unique ID that can be used to retrieve that agent manifest again.

The primary use case would be when using a Flow Designer in NiFi, the user chooses an agent class name from a list. That selection then returns one or more agent manifests that are associated with that class name. So the way I am picturing it the mapping of agent to agent manifest depends on context: from the perspecitive of an agent instance, it's going to be 1-to-1 where the manifest is always what it currently consists of. from the c2 server's perspective, it will be 1-to-many, where it is keeping track of all manifests for a given class name based on what has been uploaded to it.

These are still very new concepts, so if you have an input to help formulate them that would be welcome! This is just an initial idea and I tried to represent it the way that will support this use case. I'm sure it will change a bit before the minifi-c2-server branch could be considered "done" and merged to master.

@ApiModel
public class AgentRepositoryStatus {

private Long size;

Choose a reason for hiding this comment

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

keep in mind that java is a 64-bit two's complement so you could get values here that exceed the max. Should either comment re this or use Long's unsigned comparator methods.

Copy link
Contributor Author

@kevdoran kevdoran Mar 19, 2018

Choose a reason for hiding this comment

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

Excellent point. Will review this and update

  • For numeric primitive types, should document how it will be interpreted so that serialized values are valid and interpreted correctly when deserialized by the Java C2 (e.g., signed vs unsigned)

private Long size;
private Long sizeMax;
private Long count;
private Long countMax;

Choose a reason for hiding this comment

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

Count max seems confusing. I imagine it's not the max count seen. Realize you set a description in the model prop annotation, but you probably should tend toward a different name. Similarly sizemax and countmax seem to have overlap. Would storageMax and size max better reflect their intent?

Copy link
Contributor Author

@kevdoran kevdoran Mar 19, 2018

Choose a reason for hiding this comment

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

I'm not tied to the names of these fields. I agree that storageMax is better than sizeMax and that 'size' in general is ambiguous without reading the description. Better to avoid confusion as you point out.

I want to make this change so that the names stay consistent with the FlowQueueStatus fields if possible. My understanding (correct me if this is wrong) is that both queues and repositories have two important metrics:

  1. number of items they currently contain
  2. the data/storage footprint of those items

and that, for both queues and repositories, both of these can be given configurable caps (i.e., the "max value").

If that understanding is not correct, let me know as I'll need to correct these model classes. If it is correct, what would be the best names to use that would be consistent with how these values are labeled in MiNiFi's configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now I went with these field names:

private Long size;
private Long sizeMax;
private Long dataSize;
private Long dataSizeMax;

Thoughts?

}

public void setProvidedApiImplementations(List<DefinedType> providedApiImplementations) {
this.providedApiImplementations = providedApiImplementations;

Choose a reason for hiding this comment

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

How is this "API" implementation used? What if api implementations change will we need to require that an agreed upon versioning is used ? Is this a public API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intention of this field is to allow a component to declare that it provides an implementation of an interface defined by another DefinedType.

The goal (the details of which can change) is to allow bundles to declare interfaces/headers as DefinedTypes that other components can provide implementations for.

This is used in the NiFi UI, so will also likely be useful in the Flow Designer. In NiFi, it is used so that property descriptors can mark a property as requiring an API implementation selection as its value, so that those options can be presented in a drop down selection. That is one thought of how this will be used in this context.

Another possible use case of this is defining an interface in version 1.0 of and extension bundle and letting implementations of that interface explicitly declare support for in future versions of the bundle. For example, you could have:

MyBundle v1.0.0
    apis: [org::apache:nifi:minifi::DatabaseService]
    controllerServices: org::apache:nifi:minifi::DatabaseServiceImpl
        provides: [MyBundle/v1.0.0/org::apache:nifi:minifi::DatabaseService]

MyBundle v1.0.1
    controllerServices: org::apache:nifi:minifi::DatabaseServiceImpl
        provides: [MyBundle/v1.0.0/org::apache:nifi:minifi::DatabaseService]

MyBundle v1.1.0
    apis: [org::apache:nifi:minifi::DatabaseService]
    controllerServices: org::apache:nifi:minifi::DatabaseServiceImpl
        provides: [MyBundle/v1.0.0/org::apache:nifi:minifi::DatabaseService, 
                   MyBundle/v1.1.0/org::apache:nifi:minifi::DatabaseService]

MyBundle v2.0.0
    apis: [org::apache:nifi:minifi::DatabaseService]
    controllerServices: org::apache:nifi:minifi::DatabaseServiceImpl
        provides: [MyBundle/v2.0.0/org::apache:nifi:minifi::DatabaseService]

In this example, an API is defined in a 1.0, improved in a patch release, evolved in a minor release, and then ultimately rethought with breaking changes in a 2.0. The implementation of the ControllerService API, which happens to be in the same bundle in this example, can declare compatibility with current or previous versions.

private Boolean required;
private Boolean sensitive;
private Boolean supportsEl;
private DefinedType identifiesControllerServiceApi;

Choose a reason for hiding this comment

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

Should this be a boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, but I can see how the field name is misleading. The intention of this field is to indicate if the property value identifies a Controller Service API, and if so, which DefinedType that API is. This is used in NiFi to present a drop down menu of any ExtensionComponents that implement that API.

See the comment about which is related to this field.

If this API definition+implementation metadata complexity is not needed in this initial version of this workflow, or if it is too difficult to provide, we could leave it out and reintroduce it in a future version of C2 server.

import javax.ws.rs.core.Response;

@Component
@Path("/c2-protocol")

Choose a reason for hiding this comment

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

Is there going to be a transformation layer for different protocol types? Will that imply live in the protocol implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The internal architecture of the C2 Server will be based on these layered:

Web/Network Protocols (REST, MQTT, etc)
Services
Providers (e.g., database or persistence providers)

The bulk of the C2 Protocol implementation logic will live in the service layer, which will expose Java APIs for the Web protocol layers to interact will. The Web protocol implementations will provide very minimal/lightweight adapters, such as deserializing whatever wire-format they use into the in-memory objects that are required for calling the service layer API. External clients, such as MiNiFi agents, will only interact with the web protocols we choose to define, expose, and support. Everything else at the service level and below is internal to the C2 server implementation.

public class C2OperationAck {

private String operationId;
// TODO, result, outcome, or status of the operation? eg SUCCESS/FAIL?

Choose a reason for hiding this comment

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

yeah agreed. probably need something to provide more information.

@ApiModelProperty(
value = "The fully-qualified class type",
required = true,
notes = "For example, 'org.apache.nifi.GetFile' or 'org::apache:nifi::minifi::GetFile'")

Choose a reason for hiding this comment

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

How will this be used to define an API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above, it's really only intended to declare that a bundle contains an API definition, not the API definition itself. Presumable the interface type would reference a fully qualified class interface defined in a .h or .java file.

response = AgentClass.class,
responseContainer = "List"
)
public Response getAgentClasses() {
Copy link
Contributor Author

@kevdoran kevdoran Mar 19, 2018

Choose a reason for hiding this comment

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

  • Add ReadOnly NiFi Registry URL, Bucket, and Flow ID metadata when retrieving Agent classes (so that the flow designer knows where to read/save a flow)

@kevdoran
Copy link
Contributor Author

@phrocker Thanks for all the review feedback! You caught a lot of improvements, which I really appreciate. I've pushed an update to my branch that addresses a lot of it and I left a few questions. in reply to your comments.

If you have a chance to re-review that would be great. If you see any major issues that you would like me to address before merging, let me know and I'm happy to address them. Otherwise, I'd like to get this merged to the minifi-c2-server branch as a starting point, and we can tackle improvements (or unanswered questions for the parts that are still formulating) in follow up PRs prior to merging minifi-c2-server to master.

Thanks!

Copy link
Member

@apiri apiri left a comment

Choose a reason for hiding this comment

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

Overall looks like a great foundation. I had some minimal comments and I also saw there were a few items that @kdevdoran was looking for some additional insights on @phrocker.


@ApiModel
public class BundleManifest {

Copy link
Member

Choose a reason for hiding this comment

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

I think I like the idea of the direct access to add components. Bundles are nice for the Java side of the house but don't necessarily map well to C++. From the standpoint of the flow designer, the notions of bundles should be minimal but some defined tuple (class, version, etc) would be all that matters as a keyed extension (which has a fuller model as outlined with the other work that has gone on in this PR).

import io.swagger.annotations.ApiModelProperty;

@ApiModel("Uniform Resource Identifier for NiFi Versioned Flows saved to a NiFi Registry")
public class FlowUri {
Copy link
Member

Choose a reason for hiding this comment

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

Not sure of how this is represented by Registry but is there an actual URI form?

https://registry.myorganization.org/nifi-registry//?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it is {base_url}/buckets/{bucketId}/flows/{flowId}. We could use that in place of this object if preferred. or we could use this object but have a constructor / toString that is capable of going from/to that form.

Copy link
Member

Choose a reason for hiding this comment

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

I dig the constructor/toString idea. Gives us more flexibility and can tack things on as needed for whatever reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do!

public class Device extends DeviceInfo {

private String name;
private String firstSeen; // TODO, decide on standard JSON format for timestamps in the C2 REST API
Copy link
Member

Choose a reason for hiding this comment

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

My inclination would be to just do this in epoch millis UTC and munge it however needed.

@apiri
Copy link
Member

apiri commented Mar 22, 2018

reviewing latest commit

@apiri
Copy link
Member

apiri commented Mar 22, 2018

Changes look good here. Will get this merged. Thanks!

apiri pushed a commit to apiri/nifi-minifi that referenced this pull request Mar 22, 2018
The foundation of a data model and REST API that would support
discussed features such as a MiNiFi Agent C2 Protocol and
MiNiFi Flow Designer.

This closes apache#118.

Signed-off-by: Aldrin Piri <aldrin@apache.org>
@apiri
Copy link
Member

apiri commented Mar 22, 2018

@kevdoran This was merged in 631b599. As it is not in master this won't auto close, so if you could close this PR out at your convenience it would be appreciated!

@kevdoran kevdoran closed this Mar 22, 2018
@kevdoran
Copy link
Contributor Author

Closed. Thanks @apiri and @phrocker for helping me get this in good shape.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants