Compare commits

..

38 Commits

Author SHA1 Message Date
Sammy Kerata Oina 64f7e7f7fd NOISSUE - Refactor single algorithm processing (#117)
* Refactor single algorithm processing

Simplified the agent service's algorithm handling logic to process a single algorithm instead of multiple. This change:
- Removed the `Algorithms` type and associated stringer implementation.
- Updated the state machine and service logic to expect a singular algorithm, aligning the agent's internal state transitions with the new model.
- Adjusted the manager service and computations test server to mirror these changes in their respective payload structures, ensuring API and test consistency.
- Altered README files to reflect the simplified interaction model and removed outdated descriptions.
- Reverted the protoc-gen-go version used for generating protobuf files to maintain compatibility with the rest of the codebase.

The single-algorithm approach streamlines the computation running process, reducing complexity and potential error conditions. It directly impacts how external services will construct and send computation requests.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Update protoc-gen-go version to v1.33.0

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor variable name in computations.go and grpc.go

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-04-17 13:34:54 +02:00
Danko Miladinovic 3a14896555 NOISSUE - Attested TLS (#99)
* added initial code for attested TLS

* added client validation and verification

* fixed bugs for attested TLS

* updated README for manual testing

* fixed CI errors

* removed SNP pollicy from agent config

* added attested TLS config param to AgentConfig

* generated manager.pb.go for protoc v25.2

* updated proto-gen-go version on CI

* generated agent.pb.go to match newest proto gen version

* define errors for error handling

* fixed comments
2024-04-16 15:19:19 +02:00
Drasko DRASKOVIC b57bafd310 Fix usage (#114)
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
2024-04-16 00:42:47 +02:00
Drasko DRASKOVIC 9e6fb31d90 Fix links and add features (#113)
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
2024-04-15 22:26:41 +02:00
Sammy Kerata Oina 2b760ec207 NOISSUE - Streamline client identification (#105)
* Update protoc to v4.25.3 and streamline client identification

Protocol Buffer compiler `protoc` has been upgraded to version 4.25.3. This update unifies the version across multiple generated files to ensure compatibility and take advantage of any bug fixes and performance improvements in the new release.

Additionally, the client identification process has been refined. The redundant `WhoAmI` message and the corresponding checks have been removed from the gRPC server implementation. Clients are now identified via their address from the `Process` stream's context as soon as a connection is established, simplifying the code and potentially reducing handshake time.

This change sets the foundation for a leaner communication protocol between manager and agents, and could contribute to lower latencies in client-server interactions.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Fix goroutine bug in grpc server

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Update PROTOC_VERSION to 25.3

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor server reference in readme

Updated the deployment instructions in the README to point to the correct server location after consolidating test server documentation. Removed obsolete `manager-server` module and associated main entry point to align with the new architecture and streamline workflows.

Resolves issues with outdated links and cluttered repository structure.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-04-15 22:08:37 +02:00
Sammy Kerata Oina 2e612a2867 COCOS-108 - Enhanced state machine Synchronization (#112)
* Enhanced state machine synchronization

Introduced a wait group to the state machine to manage go-routine synchronization effectively. The Start method now ensures that the state machine's execution is tracked, preventing potential early termination of go-routines during state transitions. The test suite is updated to wait for the go-routine to complete before proceeding, ensuring more reliable test execution.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Remove unnecessary code and fix test case in agent package

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-04-15 18:55:58 +02:00
Drasko DRASKOVIC f2f57ac413 Fix license (#107)
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
2024-04-14 18:05:16 +02:00
Drasko Draskovic d50a6fcfda Fix releases link
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
2024-04-14 17:55:29 +02:00
Drasko DRASKOVIC d323fd3b7e Fix docs link (#106)
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
2024-04-14 17:49:10 +02:00
Filip Bugarski 35c2a9e92a Update cocos documentation (#89)
* Update cocos documentation

Signed-off-by: fbugarski <filipbugarski@gmail.com>

* inserted go run commands into separate lines

* updated syntax higlighting to bash

---------

Signed-off-by: fbugarski <filipbugarski@gmail.com>
Co-authored-by: Danko Miladinovic <danko@etf.rs>
2024-04-13 18:33:10 +02:00
Sammy Kerata Oina 5c406743e0 NOISSUE - Add attesatation validation/verification options (#87)
* Standardize attestation report data size to 64 bytes

Redefined the report data for attestation requests from a variable byte slice to a fixed 64-byte array across various critical components. This change enforces a standardized data length, simplifying validation logic and ensuring consistency when handling the attestation data for both the server and client end-points, logging functionalities, and the CLI tool. Updated attestation data handling in the SDK to accommodate the new fixed-length constraint.

By strictly adhering to the 64-byte requirement, the modifications promote robust input checking and prevent potential issues related to dynamic data length processing.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor attestation validation logic

Streamlined attestation validation in the CLI with a new configuration parsing approach. Introduced a retry mechanism with timeout and backoff for HTTPS getters that facilitate remote trust validation. Clarified expected data lengths in proto comments across attestation and manager services, ensuring consistency and correctness of the cryptographic data fields.

This modification enhances maintainability by replacing a verbose struct with a configurable object model, thus simplifying parameter handling. Robustness is improved through failure-retrieval strategies during remote validation, and additional in-code documentation specifies cryptographic constraints for critical data elements.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Add new attestation validation flags

Enhanced attestation validation in the CLI with additional flag support for FAMILY_ID, IMAGE_ID, REPORT_ID, REPORT_ID_MA, MEASUREMENT, CHIP_ID, and TCB-related fields. This update enables fine-grained control over attestation report criteria, ensuring stricter validation conforming to protocol requirements. Adjusted the default value for MinimumGuestSvn to align with the new policy specifications.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhance attestation policy configuration

Introduced several new configuration flags to the CLI's attestation policy, including options for minimum AMD-SP firmware builds, revocation list checking, and network restrictions. Expanded trust criteria parameters with the support for trusted author and identity key hashes. The default VMPL setting is now explicitly initialized. These changes improve security controls and offer more detailed attestation verification settings.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Simplify attestation validation process

Refactored the attestation CLI command to improve usability by removing the need for explicitly provided report data as an argument. Default values for configuration parameters have been centralized into constants for maintainability. Additionally, integrated data parsing functions to streamline the validation checks with robust error handling for cases such as invalid hex strings and file I/O issues.

This enhancement reduces user error and the complexity of entering attestation data by parsing relevant information from within the environment, all while maintaining the same security standards.

The process of marking flags as required for CLI commands has been corrected to prevent runtime errors and improve command reliability.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refined attestation validation logic

Enhanced attestation command handling by removing the hardcoded report data length and replacing it with the standard SHA-512 hash size, ensuring dynamic compatibility with hash lengths. Introduced size constants to improve code readability and enforce explicit length checks on attestation input data, raising errors when requirements are unmet. This preventative measure aims to avert potential runtime errors stemming from unexpected data sizes. The change also simplifies the minimum guest SVN description for clarity.

References to removed unnecessary whitespace maintain code cleanliness. These adjustments culminate in a more robust and maintainable attestation process, in accordance with the best practices for secure handling of cryptographic data and compliance with AMD's ABI format specifications.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhanced attestation documentation and command syntax

Updated CLI documentation to include detailed descriptions of the attestation retrieval and validation process, along with a comprehensive list of new flags for the validation command, providing users with extensive control over the attestation verification process. Updated command syntax with flags for specifying report data, improving command clarity and consistency. These changes ensure better user guidance and offer a more robust attestation handling experience.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enforce report data size validation uniformly

Refactored report data size validation to ensure it is performed consistently across the system. Removed the hard-coded length check from attestation request validation in favor of a centralized verification based on the sha512 digest size. Also, eliminated fallback mechanism to the SHA-512 hash of report data and made the length requirement explicit, leading to immediate failure if not met. This harmonizes the error handling logic, reinforces data integrity checks, and simplifies debugging by removing ambiguous length correction behavior. Adjusted related validation functions to use a common utility, enhancing maintainability.

Fix spelling errors in log messages for attestation validation commands to improve clarity in output.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Initialize attestation config with defaults

Enhanced the attestation command initialization by setting default values for the `Config` struct to prevent nil pointer exceptions and potentially streamline configuration handling. Moreover, corrected a flag declaration by removing shorthand.P support for 'permit_provisional_software', aligning it with other flags for consistency. This change should improve stability and user experience while configuring attestation policy.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* nil check prior

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix typos

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use file for attestation report

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refine attestation validation with default policy values

Adjusted default values and constants related to the attestation configuration for enhanced clarity and consistency. Changed the default minimum guest SVN value to align with new policy requirements and introduced a default guest policy constant. Additionally, standardized byte array placeholders for attestation validation fields to prevent potential issues with uninitialized bytes and ensure robust input validation checks. These changes promote more intuitive and secure attestation validation procedures.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refine attestation defaults and flag requirements

Updated the default guest policy to use hexadecimal notation for clarity and added a default minimum version for the firmware API version flag. Made 'chip_id', 'measurement', 'report_id', and 'report_id_ma' flags mandatory to ensure critical attestation data is provided by the user, enhancing validation robustness. Added a check for 'measurement' field length for consistency in input validation.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Optimize attestation file format

Standardized attestation file format across the codebase by moving the 'attestationFilePath' constant from the function scope to a higher scope within the package. Switched the file extension from .txt to .bin to reflect binary data storage, improving consistency and potential performance benefits from binary file handling.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Optimize default values for attestation validation

Removed unnecessary empty initializers and replaced hardcoded empty values with nil for optional fields in attestation validation flags. Introduced a constant defaultReportIdMa for the REPORT_ID_MA field to provide a meaningful default instead of an empty one. Streamlined the command setup by removing the requirement flags for fields that are now optional and unchecked if unset, reducing burdens on the user for providing unnecessary input.

The change simplifies the command's usage and eliminates the need for users to specify values for certain fields that can be optional, improving the command's flexibility and user experience. Additionally, setting a specific default for REPORT_ID_MA ensures clarity in cases where this is not provided by the user.

This update enhances the maintainability and usability of the attestation validation functionality.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor hardcoded attestation report size

Introduced a constant `size64` to replace the previously hardcoded array size of `64` used across various Attestation methods. This change enhances code maintainability and readability by centralizing the size definition, making it easier to update in the future if necessary.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Updated protocol buffer dependencies and cleaned CLI flags

Protobuf compiler versions were updated to v4.25.2 across various RPC service definitions for agent and manager packages, ensuring compatibility with the latest features and fixes. In the CLI attestation code, unnecessary flags (`DisallowNetwork` and `PermitProvisionalSoftware`) were removed to streamline user options, reflecting a more secure and user-focused configuration interface. Added an input validation check in `attestation.go` for CA bundle presence when a product name is set, enhancing the reliability of attestation validation. Also rectified a bug in `grpc.go` by fixing an incorrect variable assignment for reading the key file content, thereby preventing potential TLS-related errors.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Updated protoc version and adjusted proto file paths

Bumped the protocol compiler version to 25.2 to align with our dependency updates and ensure compatibility. Additionally, revised the proto file paths under the continuous integration setup, moving from 'manager/' to 'pkg/manager/' to reflect the recent directory restructuring. The changes ensure that proto file checks and comparisons are conducted in the correct file locations, preventing potential build and sync issues in future developments.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhance hash size consistency in gRPC attestation

Aligned the hard-coded byte array size for `ReportData` with `sha512.Size` constant to ensure consistency and maintainability in gRPC attestation requests and responses. This change mitigates the risk of future errors if the SHA512 hash size standard is altered.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Optimize agent-config listener loop

Removed the goroutine wrapping the listener loop in 'agent-config' manual test to streamline and simplify the connection handling logic. This change ensures the loop runs on the main goroutine, improving the readability and maintainability of the code.

Refactoring assists in avoiding potential race conditions and makes the server's flow easier to understand for future debugging and development.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor report data size checks to use constant

Centralize the report data size definition by replacing various size checks and array declarations with a constant `ReportDataSize`. This streamlines code maintenance and ensures consistency across all instances where report data size is validated or used. The changes remove direct references to the hash function output size, decoupling the report data size from the hash function's characteristics and allowing easier updates if the data size requirement changes.

These modifications bolster the codebase's flexibility for potential adjustments in security protocols or data handling specifications.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor config JSON handling in attestation CLI

Introduced an `exampleJSONConfig` constant containing a sample configuration to improve code readability and maintainability. Previously, the example JSON configuration for the attestation CLI was an inline string, making the code cluttered and less maintainable. With this change, the example configuration is now stored as a constant, resulting in cleaner command flag setup and enhanced clarity. This constant is used in the command flag description to guide users when providing their custom configuration. Additionally, refactored the command flags related to the configuration by aligning and sorting them for better code organization.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-04-09 10:33:26 +02:00
Smith Jilks b6f116474d Cocos-91 - Update Makefile and cocos documentation (#93)
* Update Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update cocos documentation

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update cocos documentation

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update cocos documentation

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Remove install rule from Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update agent README.md

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update agent README.md

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Update README.md files

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Remove INSTALL_DIR from Makefile

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

* Fix comments in README.md files

Signed-off-by: Jilks Smith <smithjilks@gmail.com>

---------

Signed-off-by: Jilks Smith <smithjilks@gmail.com>
2024-04-08 15:59:38 +02:00
Sammy Kerata Oina 3e3e02f132 Enhance security with SHA-3 and command-line args (#97)
Updated the hashing algorithm from SHA-256 to SHA-3 for improved security and reliability in the computation service. Modified the service to accept dataset and algorithm paths as command-line arguments, enhancing flexibility and user control. Increased robustness of the service's error handling by incorporating file read validations and updated usage instructions in both the README and the program's startup error messages.

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-04-06 23:45:13 +02:00
Danko Miladinovic 4b38ac09dd NOISSUE - Added SEV-SNP support (#98)
* added SEV-SNP support

* updated documentation for SEV-SNP

* changed variable names to adhere to the GO naming convention

* changed the variable name from V-sock to Vsock
2024-03-22 10:55:09 +01:00
Danko Miladinovic 1a322f8ecb NOISSUE - Fix network interface problem for agent (#96)
* Added bus/addr for pcie net

* Remove SCSI parameters

* Remove bus parameter

* Removed blank lines
2024-03-15 16:37:41 +01:00
Sammy Kerata Oina 8c2caec395 NOISSUE - Use standard GitHub templates for issues and PRs (#92)
Implemented new GitHub templates to streamline the contribution process. Bug report and feature request templates guide users in creating detailed and actionable issues, while a universal pull request template ensures systematically structured submissions. These additions facilitate more efficient reviews, and prioritize issues effectively. Additionally, configured the repository to disallow blank issues, encouraging use of the templates for better project management.

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-03-13 13:06:51 +01:00
Sammy Kerata Oina de60358c02 NOISSUE - Enforce 32-byte length check for hashes (#86)
* Enforce 32-byte length check for hashes

Introduced a validation step in the manager service to ensure both algorithm and dataset hashes are of byte length 32 before provisioning VMs, preventing runtime errors due to invalid hash lengths. The test manager-server now generates a valid 32-byte hash using SHA256 for mock data to align with the new validation requirement.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use equal comparison

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use a constant

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use constants

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-24 22:22:25 +01:00
Sammy Kerata Oina 997fb3bf48 COCOS-83 - Add hash verification for datasets and algorithms (#84)
* Add hash verification for datasets and algorithms

Enhanced data integrity checking by incorporating hash fields in Dataset and Algorithm structures, and modified the corresponding service logic to validate these hashes during processing. The update includes SHA-3 for hash computation, replacing the former SHA-256 usage, and ensures the provided data matches the expected hash from the manifest to prevent processing malformed or corrupted data.

- Introduce `Hash` field to both Dataset and Algorithm structs to store the expected hash value.
- Implement SHA-3 hashing within service methods that process the data, ensuring consistency with newly added `Hash` fields.
- Add error handling for hash mismatches, preventing further processing and alerting to potential data integrity issues.
- Update Protocol Buffers serialization to accommodate the new hash fields for gRPC communication.
- Modify manager service's Run method to pass the hash information when creating agent configurations.

Go module dependencies were updated to include the new SHA-3 package and upgrade Go version to 1.21.6 for improved stability and compatibility.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Remove identifiers from protobuf and related code

The protobuf definitions and related service handling code have been revised to drop specific identifier fields (`AlgorithmID` and `DatasetID`) to simplify API responses and internal function signatures. These removals streamline the overall data flow between components, reduce unnecessary data transmission, and lead to an aligned server-client expectation where identifiers are no longer a part of the response payload. Consequently, these changes simplify the logic within various functions and client commands, reinforcing encapsulation by ensuring that internal identifiers do not need to be managed or exposed unnecessarily.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-19 15:31:35 +01:00
Sammy Kerata Oina 722b463b6a NOISSUE - Use a single listener for logs and events (#82)
* add handler

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor gRPC and Protobuf integration for manager service

- Shifted Protobuf message definitions to a separate package `pkg/manager`.
- Updated references throughout the codebase to import and use the new package for gRPC service definitions.
- Enhanced AgentLog message with additional fields `level` and `timestamp`.
- Removed direct dependencies on old Protobuf-generated types in favor of the new package.
- Deleted obsolete Protobuf-generated files as they are now superseded by the new `pkg/manager`.
- Streamlined event publishing and gRPC handling in the manager service to use the updated Protobuf messages.

This refactoring improves modularity by centralizing Protobuf message definitions and decouples internal representation from the gRPC interface, aligning with best practices for microservice architecture. Additionally, the enriched logging structure paves the way for more detailed and fine-grained log analysis.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor vsock event/log handling and config

Streamlined event and log services in the manager by moving vsock listening functions out of `managerService` initialization and into dedicated `RetrieveAgentEventsLogs` methods. This change decouples the manager service creation from the actual start of log listening, adding clarity and flexibility in service management. Also moved logging middleware invocation outside of network handling loops to avoid unnecessary overhead.

Additionally, the agent's vsock port configuration is now dynamically passed to the `New` function in the `events` package instead of relying on a hardcoded constant, allowing for greater configurability and testability.

Finally, updated message structures for event and log sending to conform with the `ClientStreamMessage` definitions. These modifications should improve parsing and handling consistency and prepare our system for future enhancements related to inter-process communication.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix linting errors

Signed-off-by: SammyOina <sammyoina@gmail.com>

* correct path to generated files

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix comments

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove uneccessary comments

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-15 17:02:05 +01:00
Sammy Kerata Oina 938dd6cb78 NOISSUE - mTLS support across services (#71)
* Implemented mTLS support across services

Extended gRPC configuration to support mutual TLS (mTLS) in agent and manager components for enhanced security. This includes the loading of Certificate Authority (CA) certificates, server, and client certificates, and keys. Updated README documentation to reflect the new environment variables required for mTLS configuration. Additionally, streamlined secure gRPC client connection setup and logging messages to indicate whether a service is running with TLS, mTLS, or without TLS.

The change ensures secure communication between services by verifying both client and server identities, thus addressing potential security concerns in network-level interactions.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhance agent cert handling and update copyright

- Implement function to create certificate files for the agent configuration dynamically, ensuring file paths are updated to reflect newly created files. This improves the agent's setup process by automating the certificate handling.
- Update copyright clause to reflect the new owning entity, Ultraviolet, affirming correct attribution and compliance with legal requirements.
- Refactor gRPC client connection code to remove redundant package alias, streamlining the codebase and improving readability.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor cert loading with fallbacks

Removed redundant certificate file creation logic in the agent module and introduced a more robust loading mechanism in the gRPC server module to support direct byte content aside from file paths. This change simplifies the initial setup process for the agent by removing the need to create certificate files preemptively, thereby streamlining deployment in environments with varying filesystem access. It supports using certificate contents directly, enhancing compatibility with in-memory configurations or environments where file storage may not be ideal.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-08 10:07:51 +01:00
Sammy Kerata Oina e86860b9ea NOISSUE - Enhanced gRPC server concurrency with errgroup (#81)
* Enhanced gRPC server concurrency with errgroup

Refactored the gRPC server's `Process` method to handle incoming messages and service execution concurrently using an `errgroup.Group`. Added a context to the `grpcServer` struct and propagated it to support graceful shutdowns and better request handling. Changed the `Service` interface's `Run` method to use a channel for transmitting `ComputationRunReq`, facilitating non-blocking operation and streamlining the request-response cycle.

Asynchronous handling of stream requests with `errgroup` allows simultaneous processing and error management, improving the server's responsiveness and fault tolerance. This update paves the way for more robust service implementations that require concurrent operations.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove trailing space

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-08 10:05:41 +01:00
Sammy Kerata Oina e2fb7ea88d NOISSUE - Add test server (#80)
* add test server

Signed-off-by: SammyOina <sammyoina@gmail.com>

* clean up and update docs

Signed-off-by: SammyOina <sammyoina@gmail.com>

* update docs

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-07 16:16:27 +01:00
Sammy Kerata Oina 6d0100c096 COCOS-73 - Remove server (#76)
* Refactor GRPC manager service and client

The manager service and client have been restructured for stream communication, facilitating real-time agent events, logs, and run responses. The `Run` RPC is replaced by the `Process` stream RPC, enabling bidirectional streaming between clients and the manager service. This allows continuous interchange of different message types including `WhoAmIRequest`, `AgentLog`, `AgentEvent`, and `RunResponse`.

Several message types have been adjusted and new fields introduced, like `AgentPort` in `RunResponse` and various agent-config attributes including CA files and instance IDs, to support TLS client authentication and distinguish between agent instances.

We've also incorporated `google.protobuf.Timestamp` in `AgentEvent` for precise event logging. The client code reflects these modifications with updated method calls and stream handling logic for ongoing communication. Moreover, the updates necessitate corresponding changes throughout service, grpc, and sdk layers to interoperate with the new streaming approach.

The transition to streaming paves the way for a more interactive, flexible communication system that can accommodate future expansion and real-time monitoring features.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* add computation id to run response

Signed-off-by: SammyOina <sammyoina@gmail.com>

* rename request

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-07 15:32:35 +01:00
Sammy Kerata Oina 8975e28437 COCOS-77 - Stream agent logs and events over gRPC (#78)
* Refactor GRPC manager service and client

The manager service and client have been restructured for stream communication, facilitating real-time agent events, logs, and run responses. The `Run` RPC is replaced by the `Process` stream RPC, enabling bidirectional streaming between clients and the manager service. This allows continuous interchange of different message types including `WhoAmIRequest`, `AgentLog`, `AgentEvent`, and `RunResponse`.

Several message types have been adjusted and new fields introduced, like `AgentPort` in `RunResponse` and various agent-config attributes including CA files and instance IDs, to support TLS client authentication and distinguish between agent instances.

We've also incorporated `google.protobuf.Timestamp` in `AgentEvent` for precise event logging. The client code reflects these modifications with updated method calls and stream handling logic for ongoing communication. Moreover, the updates necessitate corresponding changes throughout service, grpc, and sdk layers to interoperate with the new streaming approach.

The transition to streaming paves the way for a more interactive, flexible communication system that can accommodate future expansion and real-time monitoring features.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Update GitHub Actions to Latest Versions

Upgraded GitHub Actions 'checkout' to version 4 and 'setup-go' to version 5 across various workflow files to leverage the latest features and improvements for better performance and reliability. This also ensures compatibility with Go version 1.21.x which is specified in the workflows.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor event handling and logging

Reworked event and log processing to use channels instead of direct HTTP calls. Removed obsolete events package and consolidated event structures, leading to cleaner and more maintainable code. Updated agent events to use channels, enhanced error handling in log forwarding, and simplified manager `New` function signature to accept an event channel directly.

- Removed `events` and `agentevents` packages to reduce complexity.
- Replaced direct event server communication with internal channel usage.
- Introduced `AgentEvent` struct in events.go for standardized event objects.
- Adapted `managerService` to dispatch events and logs through channels.
- Streamlined manager construction by removing the now-unnecessary event service and host IP parameters.

This change results in a more robust and easier to extend event and log management system within the agent-manager interaction.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix ci

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove unused code

Signed-off-by: SammyOina <sammyoina@gmail.com>

* add comments

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-07 12:08:39 +01:00
Sammy Kerata Oina 3e2be03047 NOISSUE - Attest validation and verification (#74)
* Expand CLI with Manager Service and Enhanced Attestation Commands

The CLI has been updated to interact not only with the Agent service but also with the Manager service, reflecting an expanded scope of operations. As part of this update, 'manager' subcommands have now been incorporated, ensuring users can seamlessly initiate computations through the manager service using the CLI.

Furthermore, attestation functionality has greatly improved. In addition to retrieving attestations from the agent, users can now validate and verify them directly from the CLI, bolstering security measures. New subcommands under 'attestation' provide clear and specific actions for attestation management.

Additionally, the command structure has been refactored to categorize commands under 'agent' or 'manager' prefixes, promoting better command organization and a more intuitive user experience.

These enhancements are part of ongoing efforts to provide robust and secure tools for service interaction and computational task management in distributed networks.

Relevant documentation adjustments and example usage have been updated in README files to align with these functional improvements.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* capitalize comments

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-07 12:02:41 +01:00
Sammy Kerata Oina 046b549079 Remove HTTP server support and streamline config (#75)
The HTTP server-related code, documentation, and configurations have been removed as part of a shift towards prioritizing gRPC for service communication. This update includes deletions of HTTP host and port configs across various components, the manager HTTP API alongside its Swagger definition, and the removal of related scaffolding and utility code. This change simplifies the overall architecture and eliminates redundant HTTP support, focusing on optimizing gRPC performance and security features.

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-07 11:03:46 +01:00
Sammy Kerata Oina 6f42135ec0 NOISSUE - Fetch attestation report (#66)
* Improve attestation and update deps

Enhanced the attestation process to require additional report data and a variable privacy level, catering to varying security requirements. The `Run` method's context parameter was removed to simplify invocation following API alignment. Dependencies got updated, notably adding `github.com/google/go-sev-guest` for attestation, and some were indirectly bumped. These updates ensure the agent service leverages the latest available features and improvements for security and stability.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Extend AttestationRequest with data fields

Enhanced the AttestationRequest message structure by introducing 'report_data' and 'vpml' fields to encapsulate additional attestation context. Adjusted service and middleware layers to incorporate these new parameters, enabling richer attestation workflows. The GRPC endpoint, logging, and metrics layers have been updated to handle the additional data, catering to more secure and nuanced attestation use cases.

Refactors Attestation command in CLI to accept report data argument and include an optional 'vpml' flag for specifying virtual machine privilege level. This change empowers users to request attestation with specific context directly from the CLI.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor attestation to remove vpml dependency

Removed the `vpml` field from the AttestationRequest protobuf, and all associated handling code, streamlining the attestation process. Code adjustments were made in gRPC client, server, and service layers to ensure proper request construction and validation that focuses only on report data. This change simplifies the attestation API and its usage, reducing error risk and improving maintainability. It reflects a decision to decouple the protocol from specific vendor measurement levels, potentially due to a shift towards a more generalized attestation procedure.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhance CLI usability and streamline SDK attestation

- Added a usage example to the CLI attestation command for better user guidance.
- Simplified the SDK attestation function by removing an unused parameter and directly populating the request structure with report data to align function parameters with actual usage requirements.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-02-06 17:55:42 +01:00
Sammy Kerata Oina e5c8243f5f COCOS-62 - Add EOS images to release (#72)
* Add build and release GitHub workflow for tagged commits

Introduced a new GitHub Actions workflow to automate building and releasing for tagged commits. The workflow checks out the required repositories, builds the project using Buildroot configurations, and creates a release with the resultant kernel and rootfs artifacts. This streamlines the release process, ensuring consistent and reproducible builds.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Update GitHub Actions to Latest Versions

Upgraded GitHub Actions in the workflow to latest or more recent versions to leverage improvements and maintain compatibility. This includes using version 4 for checkout steps and switching to 'latest' for release creation and asset upload actions, ensuring the use of up-to-date features and potential bug fixes.

Ref: Internal upgrade protocol for CI/CD consistency.
Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix workflow

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Optimized checkout step in CI workflow

Ensured the checked-out repository's reference matches the current workflow's triggering reference. This alignment can result in more predictable builds, especially when the workflow is triggered by pushes to branches other than the default or by tagged commits.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* test repo

Signed-off-by: SammyOina <sammyoina@gmail.com>

* test modified module

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Update and upgrade Ubuntu in GitHub actions workflow

Ensure the GitHub actions runner has the latest Ubuntu updates before checking out the repository. This change significantly reduces the chances of encountering bugs or security vulnerabilities stemming from outdated packages, creating a more reliable and secure CI/CD environment.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* restore repo

Signed-off-by: SammyOina <sammyoina@gmail.com>

* install golang

Signed-off-by: SammyOina <sammyoina@gmail.com>

* create tags only for release

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-31 00:08:01 +01:00
Sammy Kerata Oina 8be6129889 NOISSUE - Fix bug on receiving logs and events, update deprecated methods (#68)
* Ensure graceful shutdown and improve connection handling

Refined the network connection handling in both agent events and logs to continuously process incoming data in a loop, enabling the services to handle more than a single message per connection. Additionally, instituted a deferred close operation for the event service to guarantee resources are cleanly released upon the application's termination.

Resolves potential resource leakage and enhances log processing efficiency.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Improve robustness in event and log handling

Altered handling in agent event and log services to continue processing incoming data rather than returning early upon encountering errors. This ensures that a single erroneous data point does not prematurely halt the processing loop, improving the robustness and reliability of the services. Potential errors are now reported and logged, yet the system remains operational to handle subsequent data.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Upgrade gRPC telemetry to use StatsHandler

Switched from using gRPC's UnaryInterceptor for telemetry to the more comprehensive StatsHandler provided by otelgrpc. This enhances telemetry collection by allowing the capture of a wider range of RPC stats, leading to improved monitoring and observability of the gRPC server.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* use constants

Signed-off-by: sammy <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
Signed-off-by: sammy <sammyoina@gmail.com>
2024-01-25 15:30:27 +01:00
Sammy Kerata Oina f2567830b3 NOISSUE - Remove docker (#67)
* Remove Docker-related build functionality

Removed the Docker build scripts, Dockerfile, and docker-compose definitions from the Makefile and relevant directories. Updated documentation by stripping out references to building and running Docker containers. This change likely reflects a shift towards a different deployment strategy or a move away from Docker as a dependency for builds and runtime. This simplification could lead to less complexity in the build process and reduce maintenance overhead associated with Docker configurations.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Removed Docker image build and publish workflow

The workflow for building and publishing a Docker image on pushes to the main branch has been removed. This step likely reflects a change in deployment strategy or a move to a different CI/CD pipeline. Continuous deployment might now be managed by another service or process, adhering to updated operational requirements or infrastructure changes.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-25 15:29:16 +01:00
Sammy Kerata Oina 55c19ca6b5 NOISSUE - Refactor SDK and CLI (#69)
* Refactor CLI commands to use CLI struct

Introduced a CLI struct to encapsulate dependencies such as agentSDK and managerSDK previously passed to command constructors. This shift towards a more object-oriented approach simplifies command creation and enhances code readability. CLI-related commands are now methods on the CLI struct, accessing shared services directly, which streamlines the setup of CLI command hierarchies and reduces the need to pass dependencies around.

Removed the OpenAPI specification file as it was likely deemed obsolete or no longer necessary to be included with the CLI binary, suggesting a possible shift in how the API is managed or documented.

Lastly, the main application now sets up two distinct command groups for 'agent' and 'manager', with respective commands organized under them, making the CLI tool's structure more intuitive for users.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix ci

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-25 15:07:33 +01:00
Sammy Kerata Oina 0c77167be1 COCOS-56 - Pass agent computation events to manager (#63)
* Simplify event handling and config

Streamlined event service interface by consolidating `SendEvent` and introducing `SendRaw`. Removed `notification_server_url` and `instance_id` parameters from several event publication calls to leverage centralized event construction. This change not only cleans up redundancy in event-related code but also simplifies the configuration data flow across the system, making it easier to manage and less error-prone. Uniform event generation now improves consistency and maintainability.

Refactored configuration management in the agent and manager services. Removed notifications URL from the agent configuration, relying on a simplification that assumes a single source of events. Updated Manager Port to VsockConfigPort for clarity and consistency across vsock communication.

These modifications should facilitate easier integration and extension of event and configuration systems in the future.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor error handling in agent event forwarding

Introduced context and error channel handling to the agent event forwarding process. The logger now warns on errors during forward operations asynchronously, allowing for non-blocking error reporting. Additionally, reliance on the global logger was removed in favor of passing error information via channels, improving modularity and error flow control.

Resolves issue with silent forwarding failures by providing a means to alert system operators without halting the service. This enhancement makes the error reporting more robust and reactive while maintaining service continuity.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove unused field

Signed-off-by: SammyOina <sammyoina@gmail.com>

* update package name

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-22 16:36:34 +01:00
Sammy Kerata Oina 34c3bbdbd8 COCOS-55 - Pass agent logs to manager via vsock (#64)
* Simplify event handling and config

Streamlined event service interface by consolidating `SendEvent` and introducing `SendRaw`. Removed `notification_server_url` and `instance_id` parameters from several event publication calls to leverage centralized event construction. This change not only cleans up redundancy in event-related code but also simplifies the configuration data flow across the system, making it easier to manage and less error-prone. Uniform event generation now improves consistency and maintainability.

Refactored configuration management in the agent and manager services. Removed notifications URL from the agent configuration, relying on a simplification that assumes a single source of events. Updated Manager Port to VsockConfigPort for clarity and consistency across vsock communication.

These modifications should facilitate easier integration and extension of event and configuration systems in the future.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor error handling in agent event forwarding

Introduced context and error channel handling to the agent event forwarding process. The logger now warns on errors during forward operations asynchronously, allowing for non-blocking error reporting. Additionally, reliance on the global logger was removed in favor of passing error information via channels, improving modularity and error flow control.

Resolves issue with silent forwarding failures by providing a means to alert system operators without halting the service. This enhancement makes the error reporting more robust and reactive while maintaining service continuity.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove unused field

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Enhance agent logging via vsock connection

Redirected agent logging to use a vsock connection instead of standard output, improving the process isolation and enabling centralized log management. The change involved dialing to the specified vsock log port and initializing the logger with the vsock connection rather than stdout.

Additionally, the manager service now maintains a map of agent vsock cids to computation IDs, providing better tracking of computation resources. A routine to retrieve logs from agents was also initiated during the service setup to facilitate log collection.

Consequential to these changes is the removal of a redundant os package import in the agent's main.go, further cleaning up the dependencies.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fail gracefully

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Updated backoff strategy and VM configurations

- Added `github.com/cenkalti/backoff` to direct dependencies for robust retry logic in agent configuration sending.
- Modified the vsock logs port to align with the updated port range standards.
- Enclosed kernel console arguments in quotes to ensure proper parsing in QEMU configurations.
- Implemented exponential backoff when sending agent configurations to handle transient failures.

Refactors:
- Streamlined creation of `AgentConfig` within the computation setup to avoid unnecessary initializations when `c.AgentConfig` is not nil.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor command execution and improve argument construction

Consolidated the error handling in the command execution function for better readability. In the QEMU configuration, the argument assembly process is enhanced for clarity and correctness; the VNC parameter is now separate, and string quoting is handled properly for kernel parameters. These changes result in more maintainable code and prevent potential formatting issues during QEMU argument parsing.

Resolves issues with argument construction in QEMU config module.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refine default config handling and unpacking

Improved the agent configuration by dynamically setting default values for the log level and port if they are not specified in the incoming configuration. Also streamlined configuration unpacking in the endpoint and service layers, reducing redundancy and ensuring all required fields are correctly copied over to the Manager's configuration structure. This change ensures better fault tolerance and more maintainable code by handling edge cases where configuration values might be missing.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* rename dir

Signed-off-by: SammyOina <sammyoina@gmail.com>

* fix lint

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Ensure runRes.Empty() reflects non-empty state

Changed the always-true return value of the `runRes.Empty()` method to `false` to accurately indicate the presence of a response body. This adjustment ensures downstream handling of API responses aligns with actual content state.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Replace mglog with slog across codebase

Updated various components to replace the `mglog` logger implementation with the `slog` logger. This change affects logging initialization and calls throughout the codebase including the agent, manager, and internal server components. Transitioning to `slog` is part of a broader shift to standardize the logging mechanism to improve maintainability and consistency.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-22 14:13:47 +01:00
Sammy Kerata Oina 55afe4c038 COCOS-49 - Pass agent configuration and computation via vsock (#57)
* Optimize QEMU launch and add V-sock support

Refactored QEMU argument construction and launching logic by removing the dependency on 'agent.Computation'. This simplification makes the VM creation process more streamlined. Additionally, introduced V-sock capabilities in the QEMU configuration to facilitate improved guest-host communication. Updated the README to include kernel module setup instructions for the new V-sock feature.

The V-sock implementation enables VMs to use a consistent communication channel that is not affected by network configuration changes, enhancing reliability and potential interoperability with host services. It's important to ensure that the necessary kernel modules are loaded as part of the setup process, as documented.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Add vsock-based communication to manager

Introduced virtual socket (vsock) communication abilities in the manager package by implementing a new socket service. This includes establishing a vsock listener and stub methods for sending computation results and cleaning up resources. The addition provides the groundwork for interprocess communication between guest and host in virtualized environments.

- Integrated the `mdlayher/vsock` library for handling virtual socket operations.
- Created a new `sockService` struct to encapsulate vsock listener handling.
- Implemented `NewVsock` constructor to initialize the listener with domain value `3`.
- Added placeholder methods for future computation sending and service closing logic.

This enhancement targets scenarios where efficient VM-to-host communication is required.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* remove env

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor agent config and use vsock

Introduce `AgentConfig` struct to group agent-related configurations, and update `Computation` struct to include the new `AgentConfig` field. Replace command-line computation extraction with vsock-based config retrieval for robustness and decoupling. The agent configuration is now read from a vsock connection during runtime, allowing for more dynamic and flexible deployments. Adjusted the main agent application logic to support these configuration changes, and corresponding changes have been made in the manager to facilitate vsock communication.

This approach aligns with modern practices for microservices by streamlining configuration management and reducing reliance on static command-line parameters. Moreover, it enhances the scalability of the agent service by allowing configuration to be managed externally.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Refactor agent config and remove deprecated code

Consolidated agent configuration management into a single `AgentConfig` message and pruned deprecated Protobuf `ComputationReq`, `DatasetReq`, and `AlgorithmReq` messages. Adapted corresponding manager service logic to the new configuration structure. These modifications align with updated manager API schema, facilitate clearer configuration handling, and improve maintainability.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* send configuration

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Switch agent to listen mode for manager connections

Previously, the agent established a connection to the manager using a direct dial. This change shifts the setup to where the agent listens on a specified port and accepts incoming connections. It ensures that the agent properly handles incoming requests by initiating a listening socket and waiting for the manager to connect, enhancing the system's flexibility in connection management. This adjustment also includes graceful closure of the listening socket.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-19 17:24:51 +01:00
Danko Miladinovic 2bb3b5650a Add support for vSockets in guest kernel (#61)
* Add python pip to buildroot

* Use DHCP for agent

* Use systemd for naming network interfaces

* Removed eth0 dhcp setting

* Add ETH_IFACE variable

* Added explanation for ETH_IFACE name

* Further explain ETH_IFACE value

* Add guest kernel support for VSOCKETS
2024-01-18 14:41:05 +01:00
Sammy Kerata Oina b4a49fb56c COCOS-58 - Remove refernece to network.link file (#59)
* Optimize QEMU launch and add V-sock support

Refactored QEMU argument construction and launching logic by removing the dependency on 'agent.Computation'. This simplification makes the VM creation process more streamlined. Additionally, introduced V-sock capabilities in the QEMU configuration to facilitate improved guest-host communication. Updated the README to include kernel module setup instructions for the new V-sock feature.

The V-sock implementation enables VMs to use a consistent communication channel that is not affected by network configuration changes, enhancing reliability and potential interoperability with host services. It's important to ensure that the necessary kernel modules are loaded as part of the setup process, as documented.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Add vsock-based communication to manager

Introduced virtual socket (vsock) communication abilities in the manager package by implementing a new socket service. This includes establishing a vsock listener and stub methods for sending computation results and cleaning up resources. The addition provides the groundwork for interprocess communication between guest and host in virtualized environments.

- Integrated the `mdlayher/vsock` library for handling virtual socket operations.
- Created a new `sockService` struct to encapsulate vsock listener handling.
- Implemented `NewVsock` constructor to initialize the listener with domain value `3`.
- Added placeholder methods for future computation sending and service closing logic.

This enhancement targets scenarios where efficient VM-to-host communication is required.

Signed-off-by: SammyOina <sammyoina@gmail.com>

* Revert "Optimize QEMU launch and add V-sock support"

This reverts commit 44557c39d9.

* Revert "Add vsock-based communication to manager"

This reverts commit d1d3c01f3b.

* Remove unnecessary network link file installation

Network configuration is being handled elsewhere, making the installation of the `00-network.link` file redundant. Eliminated the line from the agent Makefile to prevent potential conflicts and streamline the initialization process.

Signed-off-by: SammyOina <sammyoina@gmail.com>

---------

Signed-off-by: SammyOina <sammyoina@gmail.com>
2024-01-17 15:14:53 +01:00
Danko Miladinovic c25cfec84c Use DHCP for VM networking (#54)
* Add python pip to buildroot

* Use DHCP for agent

* Use systemd for naming network interfaces

* Removed eth0 dhcp setting

* Add ETH_IFACE variable

* Added explanation for ETH_IFACE name

* Further explain ETH_IFACE value
2024-01-15 19:27:07 +01:00
Danko Miladinovic cca75fc5cc Add python pip to buildroot (#51) 2024-01-12 17:51:26 +01:00
94 changed files with 3798 additions and 2454 deletions
-5
View File
@@ -1,5 +1,3 @@
# Docker: Environment variables in Compose
## Jaeger
COCOS_JAEGER_PORT=6831
COCOS_JAEGER_FRONTEND=16686
@@ -8,9 +6,6 @@ COCOS_JAEGER_CONFIGS=5778
COCOS_JAEGER_URL=http://jaeger:14268/api/traces
COCOS_JAEGER_TRACE_RATIO=1.0
## Notification Server
COCOS_NOTIFICATION_SERVER_URL="http://localhost:9000"
## Core Services
### Manager
+49
View File
@@ -0,0 +1,49 @@
name: Bug Report
description: File a bug/issue report. Make sure to search to see if an issue already exists for the bug you encountered.
title: "Bug: <title>"
labels: ["bug", "needs-review", "help wanted"]
body:
- type: textarea
attributes:
label: What were you trying to achieve?
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: What are the expected results?
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: What are the received results?
description: A concise description of what you received.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: What are the steps to reproduce the issue?
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
validations:
required: false
- type: textarea
attributes:
label: In what environment did you encounter the issue?
description: A concise description of the environment you encountered the issue in.
validations:
required: true
- type: textarea
attributes:
label: Additional information you deem important
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
@@ -0,0 +1,36 @@
name: Feature Request
description: File a feature request. Make sure to search to see if a request already exists for the feature you are requesting.
title: "Feature: <title>"
labels: ["enchancement", "needs-review"]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the feature you are requesting, as well as the possible use case(s) for it.
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: dropdown
attributes:
label: Indicate the importance of this feature to you.
description: This will help us prioritize the feature request.
options:
- Must-have
- Should-have
- Nice-to-have
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the feature that you are requesting.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
+66
View File
@@ -0,0 +1,66 @@
<!--
Pull request title should be `COCOS-XXX - description` or `NOISSUE - description` where XXX is ID of the issue that this PR relate to.
Please review the [CONTRIBUTING.md](https://github.com/ultravioletrs/cocos/blob/master/CONTRIBUTING.md) file for detailed contributing guidelines.
For Work In Progress Pull Requests, please use the Draft PR feature, see https://github.blog/2019-02-14-introducing-draft-pull-requests/ for further details.
For a timely review/response, please avoid force-pushing additional commits if your PR already received reviews or comments.
- Provide tests for your changes.
- Use descriptive commit messages.
- Comment your code where appropriate.
- Squash your commits
- Update any related documentation.
-->
# What type of PR is this?
<!--This represents the type of PR you are submitting.
For example:
This is a bug fix because it fixes the following issue: #1234
This is a feature because it adds the following functionality: ...
This is a refactor because it changes the following functionality: ...
This is a documentation update because it updates the following documentation: ...
This is a dependency update because it updates the following dependencies: ...
This is an optimization because it improves the following functionality: ...
-->
## What does this do?
<!--
Please provide a brief description of what this PR is intended to do.
Include List any changes that modify/break current functionality.
-->
## Which issue(s) does this PR fix/relate to?
<!--
For pull requests that relate or close an issue, please include them below. We like to follow [Github's guidance on linking issues to pull requests](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
For example having the text: "Resolves #1234" would connect the current pull request to issue 1234. And when we merge the pull request, Github will automatically close the issue.
-->
- Related Issue #
- Resolves #
## Have you included tests for your changes?
<!--If you have not included tests, please explain why.
For example:
Yes, I have included tests for my changes.
No, I have not included tests because I do not know how to.
-->
## Did you document any new/modified feature?
<!--If you have not included documentation, please explain why.
For example:
Yes, I have updated the documentation for the new feature.
No, I have not updated the documentation because I do not know how to.
-->
### Notes
<!--Please provide any additional information you feel is important.-->
-41
View File
@@ -1,41 +0,0 @@
# Copyright (c) Ultraviolet
# SPDX-License-Identifier: Apache-2.0
name: Create and publish a Docker image
on:
push:
branches: ["main"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Manager and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
build-args: |
SVC=manager
tags: ghcr.io/ultravioletrs/cocos/manager:latest
+8 -8
View File
@@ -6,7 +6,7 @@ on:
- main
paths:
- "manager/manager.proto"
- "manager/*.pb.go"
- "pkg/manager/*.pb.go"
- "agent/agent.proto"
- "agent/*.pb.go"
pull_request:
@@ -14,7 +14,7 @@ on:
- main
paths:
- "manager/manager.proto"
- "manager/*.pb.go"
- "pkg/manager/*.pb.go"
- "agent/agent.proto"
- "agent/*.pb.go"
@@ -24,17 +24,17 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.21.x
- name: Set up protoc
run: |
PROTOC_VERSION=25.1
PROTOC_GEN_VERSION=v1.31.0
PROTOC_VERSION=25.3
PROTOC_GEN_VERSION=v1.33.0
PROTOC_GRPC_VERSION=v1.3.0
# Download and install protoc
@@ -55,7 +55,7 @@ jobs:
- name: Set up Cocos-AI
run: |
# Rename .pb.go files to .pb.go.tmp to prevent conflicts
for p in $(ls manager/*.pb.go); do
for p in $(ls pkg/manager/*.pb.go); do
mv $p $p.tmp
done
@@ -67,7 +67,7 @@ jobs:
make protoc
# Compare generated Go files with the original ones
for p in $(ls manager/*.pb.go); do
for p in $(ls pkg/manager/*.pb.go); do
if ! cmp -s $p $p.tmp; then
echo "Proto file and generated Go file $p are out of sync!"
exit 1
+73
View File
@@ -0,0 +1,73 @@
name: Build and Release
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Update Ubuntu
run: |
sudo apt-get update
sudo apt-get upgrade -y
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache-dependency-path: "go.sum"
- name: Checkout cocos
uses: actions/checkout@v4
with:
repository: 'ultravioletrs/cocos'
path: cocos
- name: Checkout buildroot
uses: actions/checkout@v4
with:
repository: 'buildroot/buildroot'
path: buildroot
- name: Build
run: |
cd buildroot
make BR2_EXTERNAL=../cocos/hal/linux cocos_defconfig
make
- name: Create Release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-kernel
uses: actions/upload-release-asset@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./buildroot/output/images/bzImage
asset_name: bzImage
asset_content_type: application/octet-stream
- name: Upload Release Asset
id: upload-release-rootfs
uses: actions/upload-release-asset@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./buildroot/output/images/rootfs.cpio.gz
asset_name: rootfs.cpio.gz
asset_content_type: application/gzip
+2 -2
View File
@@ -14,10 +14,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.21.x
+87
View File
@@ -0,0 +1,87 @@
# Contributing to CoCos
The following is a set of guidelines to contribute to CoCos and its libraries, which are
hosted on the [Ultraviolet Organization](https://github.com/ultravioletrs) on GitHub.
This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0).
By participating, you are expected to uphold this code. Please report unacceptable behavior to
[abuse@ultraviolet.rs](mailto:abuse@ultraviolet.rs).
## Reporting issues
Reporting issues are a great way to contribute to the project. We are perpetually grateful about a well-written,
thorough bug report.
Before raising a new issue, check [our issue
list](https://github.com/ultravioletrs/cocos/issues) to determine if it already contains the
problem that you are facing.
A good bug report shouldn't leave others needing to chase you for more information. Please be as detailed as possible. The following questions might serve as a template for writing a detailed
report:
- What were you trying to achieve?
- What are the expected results?
- What are the received results?
- What are the steps to reproduce the issue?
- In what environment did you encounter the issue?
## Pull requests
Good pull requests (e.g. patches, improvements, new features) are a fantastic help. They should
remain focused in scope and avoid unrelated commits.
**Please ask first** before embarking on any significant pull request (e.g. implementing new features,
refactoring code etc.), otherwise you risk spending a lot of time working on something that the
maintainers might not want to merge into the project.
Please adhere to the coding conventions used throughout the project. If in doubt, consult the
[Effective Go](https://golang.org/doc/effective_go.html) style guide.
To contribute to the project, [fork](https://help.github.com/articles/fork-a-repo/) it,
clone your fork repository, and configure the remotes:
```
git clone https://github.com/<your-username>/cocos.git
cd cocos
git remote add upstream https://github.com/ultravioletrs/cocos.git
```
If your cloned repository is behind the upstream commits, then get the latest changes from upstream:
```
git checkout master
git pull --rebase upstream main
```
Create a new topic branch from `master` using the naming convention `COCOS-[issue-number]`
to help us keep track of your contribution scope:
```
git checkout -b COCOS-[issue-number]
```
Commit your changes in logical chunks. When you are ready to commit, make sure
to write a Good Commit Message™. Consult the [Erlang's contributing guide](https://github.com/erlang/otp/wiki/Writing-good-commit-messages)
if you're unsure of what constitutes a Good Commit Message™. Use [interactive rebase](https://help.github.com/articles/about-git-rebase)
to group your commits into logical units of work before making it public.
Note that every commit you make must be signed. By signing off your work you indicate that you
are accepting the [Developer Certificate of Origin](https://developercertificate.org/).
Use your real name (sorry, no pseudonyms or anonymous contributions). If you set your `user.name`
and `user.email` git configs, you can sign your commit automatically with `git commit -s`.
Locally merge (or rebase) the upstream development branch into your topic branch:
```
git pull --rebase upstream main
```
Push your topic branch up to your fork:
```
git push origin COCOS-[issue-number]
```
[Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title
and detailed description.
View File
+2 -50
View File
@@ -5,13 +5,6 @@ GOARCH ?= amd64
VERSION ?= $(shell git describe --abbrev=0 --tags --always)
COMMIT ?= $(shell git rev-parse HEAD)
TIME ?= $(shell date +%F_%T)
CLI_SOURCE = ./cmd/cli/main.go
CLI_BIN = ${BUILD_DIR}/cocos-cli
DOCKERS = $(addprefix docker_,$(SERVICES))
DOCKERS_DEV = $(addprefix docker_dev_,$(SERVICES))
COCOS_DOCKER_IMAGE_NAME_PREFIX=ghcr.io/ultravioletrs/cocos/
USER_REPO ?= $(shell git remote get-url origin | sed -e 's/.*\/\([^/]*\)\/\([^/]*\).*/\1_\2/' )
empty:=
space:= $(empty) $(empty)
@@ -24,54 +17,13 @@ define compile_service
-o ${BUILD_DIR}/cocos-$(1) cmd/$(1)/main.go
endef
define make_docker
$(eval svc=$(subst docker_,,$(1)))
docker build \
--no-cache \
--build-arg SVC=$(svc) \
--build-arg GOARCH=$(GOARCH) \
--build-arg GOARM=$(GOARM) \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
--build-arg TIME=$(TIME) \
--tag=$(COCOS_DOCKER_IMAGE_NAME_PREFIX)$(svc) \
-f docker/Dockerfile .
endef
define make_docker_dev
$(eval svc=$(subst docker_dev_,,$(1)))
docker build \
--no-cache \
--build-arg SVC=$(svc) \
--tag=$(COCOS_DOCKER_IMAGE_NAME_PREFIX)$(svc) \
-f docker/Dockerfile.dev ./build
endef
.PHONY: all $(SERVICES) dockers dockers_dev
.PHONY: all $(SERVICES)
all: $(SERVICES)
$(DOCKERS):
$(call make_docker,$(@),$(GOARCH))
$(DOCKERS_DEV):
$(call make_docker_dev,$(@))
dockers: $(DOCKERS)
dockers_dev: $(DOCKERS_DEV)
$(SERVICES):
$(call compile_service,$(@))
install-cli: cli
cp ${CLI_BIN} ~/.local/bin/cocos-cli
protoc:
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative agent/agent.proto
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative manager/manager.proto
run:
docker compose -f docker/docker-compose.yml -p cocos up
protoc -I. --go_out=./pkg --go_opt=paths=source_relative --go-grpc_out=./pkg --go-grpc_opt=paths=source_relative manager/manager.proto
+29 -33
View File
@@ -12,54 +12,50 @@ Data is always encrypted, protected by hardware secure enclaves (Trusted Executi
attested via secure remote attestation protocols, and invisible to cloud processors or any other
3rd party to which computation is offloaded.
## Install
The following prerequisites are needed to run Cocos:
## Features
- [Docker](https://docs.docker.com/install/)
- [Docker compose](https://docs.docker.com/compose/install/)
Cocos AI is implementing the following features:
### Build Docker Images
Currenty, there is no Docker registry, so Cocos images must be built by hand:
```
make dockers
```
For this you might be needing to setup DNS servers in your `/etc/resolf.conf` as explained
[here](https://github.com/docker/cli/issues/2618) (i.e. add Google's `nameserver 8.8.8.8`).
### Run Composition
Once the images are built (`docker images` command should show you `ghcr.io/ultravioletrs/cocos/manager`),
composition can be run:
```bash
make run
```
This will bring up the Cocos docker services and interconnect them.
- TEE enablement, deployment and monitoring (secure VM manager)
- HAL for TEEs based on hardened Linux kernel, secure bootloader and custom-tailored embedded rootfs for minimal TCB
- In-enclave agent, netowrking controller and other system software
- Encrypted asynchronous data transfer and result delivery
- API for programmable platform manipulation
- HW and SW supported attestation with verification tools
- CLI for system interaction
## Usage
The quickest way to start using Cocos is via the CLI. The latest version can be downloaded from the [official releases page][rel].
It can also be built and used from the project's root directory:
Clone the repo and create binaries:
```bash
make cli
./build/cocos-cli version
git clone git@github.com:ultravioletrs/cocos.git
make
```
Additional details on using the CLI can be found in the [CLI documentation](https://docs.cocos.ai/cli).
This will create 3 binaries:
```bash
ls build/
# cocos-agent cocos-cli cocos-manager
```
- Manager can be deployed on the AMD SEV-SNP host
- Agent can be built into [EOS][eos]-based HAL
- CLI can be used to communicate to remote Agent.
## Documentation
Official documentation is hosted at [Cocos official docs page][docs]. Documentation is auto-generated, checkout the instructions on [official docs repository](https://github.com/ultravioletrs/docs).
Project documentation is hosted at [Cocos AI official docs page][docs].
Documentation is generated from the [docs repository](https://github.com/ultravioletrs/docs).
## License
Cocos AI is a proprietary product created by Ultraviolet company.
Cocos AI is published under permissive open-source [Apache-2.0](LICENSE) license.
[cc]: https://confidentialcomputing.io/white-papers-reports/
[cocos]: https://cocos.ai/
[rel]: https://github.com/ultraviolet/cocos/releases
[rel]: https://github.com/ultravioletrs/cocos/releases
[tee]: https://en.wikipedia.org/wiki/Trusted_execution_environment
[docs]: https://docs.cocos.ultraviolet.rs
[cli]: https://docs.cocos.ultraviolet.rs/cli
[eos]: https://github.com/ultravioletrs/eos
+7 -10
View File
@@ -9,15 +9,12 @@ The service is configured using the environment variables from the following tab
| Variable | Description | Default |
| ----------------------------- | ------------------------------------------------------ | ------------------------------ |
| AGENT_LOG_LEVEL | Log level for agent service (debug, info, warn, error) | info |
| AGENT_HTTP_HOST | Agent service HTTP host | "" |
| AGENT_HTTP_PORT | Agent service HTTP port | 9031 |
| AGENT_HTTP_SERVER_CERT | Path to HTTP server certificate in pem format | "" |
| AGENT_HTTP_SERVER_KEY | Path to HTTP server key in pem format | "" |
| AGENT_GRPC_HOST | Agent service gRPC host | "" |
| AGENT_GRPC_PORT | Agent service gRPC port | 7002 |
| AGENT_GRPC_SERVER_CERT | Path to gRPC server certificate in pem format | "" |
| AGENT_GRPC_SERVER_KEY | Path to gRPC server key in pem format | "" |
| COCOS_NOTIFICATION_SERVER_URL | Server to receive notification events from agent. | http:/localhost:9000 |
| AGENT_GRPC_SERVER_CA_CERTS | Path to gRPC server CA certificate | "" |
| AGENT_GRPC_CLIENT_CA_CERTS | Path to gRPC client CA certificate | "" |
## Deployment
@@ -25,15 +22,15 @@ The service is configured using the environment variables from the following tab
To start the service outside of the container, execute the following shell script:
```bash
# download the latest version of the service
go get github.com/ultravioletrs/cocos
# Download the latest version of the service
git clone git@github.com:ultravioletrs/cocos.git
cd $GOPATH/src/github.com/ultravioletrs/cocos
cd cocos
# compile the agent
# Compile the service
make agent
# set the environment variables and run the service
# Run the service
./build/cocos-agent
```
+57 -328
View File
@@ -3,8 +3,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v4.25.1
// protoc-gen-go v1.33.0
// protoc v4.25.3
// source: agent/agent.proto
package agent
@@ -90,8 +90,6 @@ type AlgoResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AlgorithmID string `protobuf:"bytes,1,opt,name=algorithmID,proto3" json:"algorithmID,omitempty"`
}
func (x *AlgoResponse) Reset() {
@@ -126,13 +124,6 @@ func (*AlgoResponse) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{1}
}
func (x *AlgoResponse) GetAlgorithmID() string {
if x != nil {
return x.AlgorithmID
}
return ""
}
type DataRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -200,8 +191,6 @@ type DataResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DatasetID string `protobuf:"bytes,1,opt,name=datasetID,proto3" json:"datasetID,omitempty"`
}
func (x *DataResponse) Reset() {
@@ -236,13 +225,6 @@ func (*DataResponse) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{3}
}
func (x *DataResponse) GetDatasetID() string {
if x != nil {
return x.DatasetID
}
return ""
}
type ResultRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -341,6 +323,8 @@ type AttestationRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ReportData []byte `protobuf:"bytes,1,opt,name=report_data,json=reportData,proto3" json:"report_data,omitempty"` // Should be of length 64.
}
func (x *AttestationRequest) Reset() {
@@ -375,6 +359,13 @@ func (*AttestationRequest) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{6}
}
func (x *AttestationRequest) GetReportData() []byte {
if x != nil {
return x.ReportData
}
return nil
}
type AttestationResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -422,203 +413,6 @@ func (x *AttestationResponse) GetFile() []byte {
return nil
}
type ComputationReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Datasets []*DatasetReq `protobuf:"bytes,4,rep,name=datasets,proto3" json:"datasets,omitempty"`
Algorithms []*AlgorithmReq `protobuf:"bytes,5,rep,name=algorithms,proto3" json:"algorithms,omitempty"`
ResultConsumers []string `protobuf:"bytes,6,rep,name=result_consumers,json=resultConsumers,proto3" json:"result_consumers,omitempty"`
}
func (x *ComputationReq) Reset() {
*x = ComputationReq{}
if protoimpl.UnsafeEnabled {
mi := &file_agent_agent_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ComputationReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ComputationReq) ProtoMessage() {}
func (x *ComputationReq) ProtoReflect() protoreflect.Message {
mi := &file_agent_agent_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ComputationReq.ProtoReflect.Descriptor instead.
func (*ComputationReq) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{8}
}
func (x *ComputationReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *ComputationReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ComputationReq) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *ComputationReq) GetDatasets() []*DatasetReq {
if x != nil {
return x.Datasets
}
return nil
}
func (x *ComputationReq) GetAlgorithms() []*AlgorithmReq {
if x != nil {
return x.Algorithms
}
return nil
}
func (x *ComputationReq) GetResultConsumers() []string {
if x != nil {
return x.ResultConsumers
}
return nil
}
type DatasetReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *DatasetReq) Reset() {
*x = DatasetReq{}
if protoimpl.UnsafeEnabled {
mi := &file_agent_agent_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DatasetReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DatasetReq) ProtoMessage() {}
func (x *DatasetReq) ProtoReflect() protoreflect.Message {
mi := &file_agent_agent_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DatasetReq.ProtoReflect.Descriptor instead.
func (*DatasetReq) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{9}
}
func (x *DatasetReq) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *DatasetReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type AlgorithmReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *AlgorithmReq) Reset() {
*x = AlgorithmReq{}
if protoimpl.UnsafeEnabled {
mi := &file_agent_agent_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AlgorithmReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AlgorithmReq) ProtoMessage() {}
func (x *AlgorithmReq) ProtoReflect() protoreflect.Message {
mi := &file_agent_agent_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AlgorithmReq.ProtoReflect.Descriptor instead.
func (*AlgorithmReq) Descriptor() ([]byte, []int) {
return file_agent_agent_proto_rawDescGZIP(), []int{10}
}
func (x *AlgorithmReq) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *AlgorithmReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
var File_agent_agent_proto protoreflect.FileDescriptor
var file_agent_agent_proto_rawDesc = []byte{
@@ -629,66 +423,42 @@ var file_agent_agent_proto_rawDesc = []byte{
0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x22, 0x30, 0x0a, 0x0c, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d,
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69,
0x74, 0x68, 0x6d, 0x49, 0x44, 0x22, 0x53, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x12, 0x1a,
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2c, 0x0a, 0x0c, 0x44, 0x61,
0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61,
0x74, 0x61, 0x73, 0x65, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64,
0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x49, 0x44, 0x22, 0x2b, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x75,
0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e,
0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e,
0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, 0x24, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x41,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x35, 0x0a, 0x12, 0x41,
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x22, 0x29, 0x0a, 0x13, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0xe5, 0x01, 0x0a,
0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74,
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61,
0x73, 0x65, 0x74, 0x73, 0x12, 0x33, 0x0a, 0x0a, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x65, 0x71, 0x52, 0x0a, 0x61,
0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20,
0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x75,
0x6d, 0x65, 0x72, 0x73, 0x22, 0x38, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x52,
0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3a,
0x0a, 0x0c, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x65, 0x71, 0x12, 0x1a,
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xf5, 0x01, 0x0a, 0x0c, 0x41,
0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x41,
0x6c, 0x67, 0x6f, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x6c, 0x67, 0x6f,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
0x41, 0x6c, 0x67, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31,
0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44,
0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65,
0x6e, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x37, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x41, 0x74,
0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x61, 0x67, 0x65, 0x6e,
0x74, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x74, 0x74,
0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61,
0x74, 0x61, 0x22, 0x29, 0x0a, 0x13, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x32, 0xf5, 0x01,
0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x31,
0x0a, 0x04, 0x41, 0x6c, 0x67, 0x6f, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41,
0x6c, 0x67, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65,
0x6e, 0x74, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x31, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e,
0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e,
0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14,
0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a,
0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x61,
0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -703,7 +473,7 @@ func file_agent_agent_proto_rawDescGZIP() []byte {
return file_agent_agent_proto_rawDescData
}
var file_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_agent_agent_proto_goTypes = []interface{}{
(*AlgoRequest)(nil), // 0: agent.AlgoRequest
(*AlgoResponse)(nil), // 1: agent.AlgoResponse
@@ -713,26 +483,21 @@ var file_agent_agent_proto_goTypes = []interface{}{
(*ResultResponse)(nil), // 5: agent.ResultResponse
(*AttestationRequest)(nil), // 6: agent.AttestationRequest
(*AttestationResponse)(nil), // 7: agent.AttestationResponse
(*ComputationReq)(nil), // 8: agent.ComputationReq
(*DatasetReq)(nil), // 9: agent.DatasetReq
(*AlgorithmReq)(nil), // 10: agent.AlgorithmReq
}
var file_agent_agent_proto_depIdxs = []int32{
9, // 0: agent.ComputationReq.datasets:type_name -> agent.DatasetReq
10, // 1: agent.ComputationReq.algorithms:type_name -> agent.AlgorithmReq
0, // 2: agent.AgentService.Algo:input_type -> agent.AlgoRequest
2, // 3: agent.AgentService.Data:input_type -> agent.DataRequest
4, // 4: agent.AgentService.Result:input_type -> agent.ResultRequest
6, // 5: agent.AgentService.Attestation:input_type -> agent.AttestationRequest
1, // 6: agent.AgentService.Algo:output_type -> agent.AlgoResponse
3, // 7: agent.AgentService.Data:output_type -> agent.DataResponse
5, // 8: agent.AgentService.Result:output_type -> agent.ResultResponse
7, // 9: agent.AgentService.Attestation:output_type -> agent.AttestationResponse
6, // [6:10] is the sub-list for method output_type
2, // [2:6] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
0, // 0: agent.AgentService.Algo:input_type -> agent.AlgoRequest
2, // 1: agent.AgentService.Data:input_type -> agent.DataRequest
4, // 2: agent.AgentService.Result:input_type -> agent.ResultRequest
6, // 3: agent.AgentService.Attestation:input_type -> agent.AttestationRequest
1, // 4: agent.AgentService.Algo:output_type -> agent.AlgoResponse
3, // 5: agent.AgentService.Data:output_type -> agent.DataResponse
5, // 6: agent.AgentService.Result:output_type -> agent.ResultResponse
7, // 7: agent.AgentService.Attestation:output_type -> agent.AttestationResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_agent_agent_proto_init() }
@@ -837,42 +602,6 @@ func file_agent_agent_proto_init() {
return nil
}
}
file_agent_agent_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ComputationReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_agent_agent_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DatasetReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_agent_agent_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AlgorithmReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -880,7 +609,7 @@ func file_agent_agent_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_agent_agent_proto_rawDesc,
NumEnums: 0,
NumMessages: 11,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
+8 -21
View File
@@ -20,7 +20,7 @@ message AlgoRequest {
string id = 3;
}
message AlgoResponse { string algorithmID = 1; }
message AlgoResponse {}
message DataRequest {
bytes dataset = 1;
@@ -28,33 +28,20 @@ message DataRequest {
string id = 3;
}
message DataResponse { string datasetID = 1; }
message DataResponse {}
message ResultRequest {
string consumer = 1;
}
message ResultResponse { bytes file = 1; }
message AttestationRequest { }
message AttestationResponse { bytes file = 1; }
message ComputationReq {
string id = 1;
string name = 2;
string description = 3;
repeated DatasetReq datasets = 4;
repeated AlgorithmReq algorithms = 5;
repeated string result_consumers = 6;
message ResultResponse {
bytes file = 1;
}
message DatasetReq {
string provider = 1;
string id = 2;
message AttestationRequest {
bytes report_data = 1; // Should be of length 64.
}
message AlgorithmReq {
string provider = 1;
string id = 2;
message AttestationResponse {
bytes file = 1;
}
+1 -1
View File
@@ -4,7 +4,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1
// - protoc v4.25.3
// source: agent/agent.proto
package agent
+14 -17
View File
@@ -80,14 +80,12 @@ func encodeAlgoRequest(_ context.Context, request interface{}) (interface{}, err
// decodeAlgoResponse is a transport/grpc.DecodeResponseFunc that
// converts a gRPC AlgoResponse to a user-domain response.
func decodeAlgoResponse(_ context.Context, grpcResponse interface{}) (interface{}, error) {
response, ok := grpcResponse.(*agent.AlgoResponse)
_, ok := grpcResponse.(*agent.AlgoResponse)
if !ok {
return nil, fmt.Errorf("invalid response type: %T", grpcResponse)
}
return algoRes{
AlgorithmID: response.AlgorithmID,
}, nil
return algoRes{}, nil
}
// encodeDataRequest is a transport/grpc.EncodeRequestFunc that
@@ -108,14 +106,12 @@ func encodeDataRequest(_ context.Context, request interface{}) (interface{}, err
// decodeDataResponse is a transport/grpc.DecodeResponseFunc that
// converts a gRPC DataResponse to a user-domain response.
func decodeDataResponse(_ context.Context, grpcResponse interface{}) (interface{}, error) {
response, ok := grpcResponse.(*agent.DataResponse)
_, ok := grpcResponse.(*agent.DataResponse)
if !ok {
return nil, fmt.Errorf("invalid response type: %T", grpcResponse)
}
return dataRes{
DatasetID: response.DatasetID,
}, nil
return dataRes{}, nil
}
// encodeResultRequest is a transport/grpc.EncodeRequestFunc that
@@ -147,8 +143,11 @@ func decodeResultResponse(_ context.Context, grpcResponse interface{}) (interfac
// encodeAttestationRequest is a transport/grpc.EncodeRequestFunc that
// converts a user-domain attestationReq to a gRPC request.
func encodeAttestationRequest(_ context.Context, request interface{}) (interface{}, error) {
// No request parameters needed for retrieving computation result file.
return &agent.AttestationRequest{}, nil
req, ok := request.(*attestationReq)
if !ok {
return nil, fmt.Errorf("invalid request type: %T", request)
}
return &agent.AttestationRequest{ReportData: req.ReportData[:]}, nil
}
// decodeAttestationResponse is a transport/grpc.DecodeResponseFunc that
@@ -169,13 +168,12 @@ func (c grpcClient) Algo(ctx context.Context, request *agent.AlgoRequest, _ ...g
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
res, err := c.algo(ctx, &algoReq{Algorithm: request.Algorithm, Provider: request.Provider, Id: request.Id})
_, err := c.algo(ctx, &algoReq{Algorithm: request.Algorithm, Provider: request.Provider, Id: request.Id})
if err != nil {
return nil, err
}
algoRes := res.(algoRes)
return &agent.AlgoResponse{AlgorithmID: algoRes.AlgorithmID}, nil
return &agent.AlgoResponse{}, nil
}
// Data implements the Data method of the agent.AgentServiceClient interface.
@@ -183,13 +181,12 @@ func (c grpcClient) Data(ctx context.Context, request *agent.DataRequest, _ ...g
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
res, err := c.data(ctx, &dataReq{Dataset: request.Dataset, Provider: request.Provider, Id: request.Id})
_, err := c.data(ctx, &dataReq{Dataset: request.Dataset, Provider: request.Provider, Id: request.Id})
if err != nil {
return nil, err
}
dataRes := res.(dataRes)
return &agent.DataResponse{DatasetID: dataRes.DatasetID}, nil
return &agent.DataResponse{}, nil
}
// Result implements the Result method of the agent.AgentServiceClient interface.
@@ -211,7 +208,7 @@ func (c grpcClient) Attestation(ctx context.Context, request *agent.AttestationR
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
res, err := c.attestation(ctx, &attestationReq{})
res, err := c.attestation(ctx, &attestationReq{ReportData: [agent.ReportDataSize]byte(request.ReportData)})
if err != nil {
return nil, err
}
+5 -5
View File
@@ -19,12 +19,12 @@ func algoEndpoint(svc agent.Service) endpoint.Endpoint {
algo := agent.Algorithm{Algorithm: req.Algorithm, Provider: req.Provider, ID: req.Id}
algorithmID, err := svc.Algo(ctx, algo)
err := svc.Algo(ctx, algo)
if err != nil {
return algoRes{}, err
}
return algoRes{AlgorithmID: algorithmID}, nil
return algoRes{}, nil
}
}
@@ -38,12 +38,12 @@ func dataEndpoint(svc agent.Service) endpoint.Endpoint {
dataset := agent.Dataset{Dataset: req.Dataset, Provider: req.Provider, ID: req.Id}
datasetID, err := svc.Data(ctx, dataset)
err := svc.Data(ctx, dataset)
if err != nil {
return dataRes{}, err
}
return dataRes{DatasetID: datasetID}, nil
return dataRes{}, nil
}
}
@@ -70,7 +70,7 @@ func attestationEndpoint(svc agent.Service) endpoint.Endpoint {
if err := req.validate(); err != nil {
return attestationRes{}, err
}
file, err := svc.Attestation(ctx)
file, err := svc.Attestation(ctx, req.ReportData)
if err != nil {
return attestationRes{}, err
}
+1 -2
View File
@@ -54,10 +54,9 @@ func (req resultReq) validate() error {
}
type attestationReq struct {
// No request parameters needed for retrieving attestation output
ReportData [64]byte
}
func (req attestationReq) validate() error {
// No request parameters to validate, so no validation logic needed
return nil
}
+2 -6
View File
@@ -2,13 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
package grpc
type algoRes struct {
AlgorithmID string `json:"algorithmId,omitempty"`
}
type algoRes struct{}
type dataRes struct {
DatasetID string `json:"datasetId,omitempty"`
}
type dataRes struct{}
type resultRes struct {
File []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+8 -10
View File
@@ -4,6 +4,7 @@ package grpc
import (
"context"
"errors"
"github.com/go-kit/kit/transport/grpc"
"github.com/ultravioletrs/cocos/agent"
@@ -54,10 +55,7 @@ func decodeAlgoRequest(_ context.Context, grpcReq interface{}) (interface{}, err
}
func encodeAlgoResponse(_ context.Context, response interface{}) (interface{}, error) {
res := response.(algoRes)
return &agent.AlgoResponse{
AlgorithmID: res.AlgorithmID,
}, nil
return &agent.AlgoResponse{}, nil
}
func decodeDataRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -71,10 +69,7 @@ func decodeDataRequest(_ context.Context, grpcReq interface{}) (interface{}, err
}
func encodeDataResponse(_ context.Context, response interface{}) (interface{}, error) {
res := response.(dataRes)
return &agent.DataResponse{
DatasetID: res.DatasetID,
}, nil
return &agent.DataResponse{}, nil
}
func decodeResultRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -90,8 +85,11 @@ func encodeResultResponse(_ context.Context, response interface{}) (interface{},
}
func decodeAttestationRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
// No fields to extract from gRPC request, so returning an empty struct
return attestationReq{}, nil
req := grpcReq.(*agent.AttestationRequest)
if len(req.ReportData) != agent.ReportDataSize {
return nil, errors.New("malformed report data, expect 64 bytes")
}
return attestationReq{ReportData: [agent.ReportDataSize]byte(req.ReportData)}, nil
}
func encodeAttestationResponse(_ context.Context, response interface{}) (interface{}, error) {
+7 -20
View File
@@ -9,38 +9,25 @@ package api
import (
"context"
"fmt"
"log/slog"
"time"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/agent"
)
var _ agent.Service = (*loggingMiddleware)(nil)
type loggingMiddleware struct {
logger mglog.Logger
logger *slog.Logger
svc agent.Service
}
// LoggingMiddleware adds logging facilities to the core service.
func LoggingMiddleware(svc agent.Service, logger mglog.Logger) agent.Service {
func LoggingMiddleware(svc agent.Service, logger *slog.Logger) agent.Service {
return &loggingMiddleware{logger, svc}
}
func (lm *loggingMiddleware) Run(ctx context.Context, ac agent.Computation) (response string, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method Run for computation %s took %s to complete", ac.ID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.Run(ctx, ac)
}
func (lm *loggingMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm) (response string, err error) {
func (lm *loggingMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method Algo took %s to complete", time.Since(begin))
if err != nil {
@@ -53,7 +40,7 @@ func (lm *loggingMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm
return lm.svc.Algo(ctx, algorithm)
}
func (lm *loggingMiddleware) Data(ctx context.Context, dataset agent.Dataset) (response string, err error) {
func (lm *loggingMiddleware) Data(ctx context.Context, dataset agent.Dataset) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method Data took %s to complete", time.Since(begin))
if err != nil {
@@ -79,7 +66,7 @@ func (lm *loggingMiddleware) Result(ctx context.Context, consumer string) (respo
return lm.svc.Result(ctx, consumer)
}
func (lm *loggingMiddleware) Attestation(ctx context.Context) (response []byte, err error) {
func (lm *loggingMiddleware) Attestation(ctx context.Context, reportData [agent.ReportDataSize]byte) (response []byte, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method Attestation took %s to complete", time.Since(begin))
if err != nil {
@@ -89,5 +76,5 @@ func (lm *loggingMiddleware) Attestation(ctx context.Context) (response []byte,
lm.logger.Info(fmt.Sprintf("%s without errors", message))
}(time.Now())
return lm.svc.Attestation(ctx)
return lm.svc.Attestation(ctx, reportData)
}
+4 -13
View File
@@ -32,16 +32,7 @@ func MetricsMiddleware(svc agent.Service, counter metrics.Counter, latency metri
}
}
func (ms *metricsMiddleware) Run(ctx context.Context, ac agent.Computation) (string, error) {
defer func(begin time.Time) {
ms.counter.With("method", "run").Add(1)
ms.latency.With("method", "run").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.Run(ctx, ac)
}
func (ms *metricsMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm) (string, error) {
func (ms *metricsMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm) error {
defer func(begin time.Time) {
ms.counter.With("method", "algo").Add(1)
ms.latency.With("method", "algo").Observe(time.Since(begin).Seconds())
@@ -50,7 +41,7 @@ func (ms *metricsMiddleware) Algo(ctx context.Context, algorithm agent.Algorithm
return ms.svc.Algo(ctx, algorithm)
}
func (ms *metricsMiddleware) Data(ctx context.Context, dataset agent.Dataset) (string, error) {
func (ms *metricsMiddleware) Data(ctx context.Context, dataset agent.Dataset) error {
defer func(begin time.Time) {
ms.counter.With("method", "data").Add(1)
ms.latency.With("method", "data").Observe(time.Since(begin).Seconds())
@@ -68,11 +59,11 @@ func (ms *metricsMiddleware) Result(ctx context.Context, consumer string) ([]byt
return ms.svc.Result(ctx, consumer)
}
func (ms *metricsMiddleware) Attestation(ctx context.Context) ([]byte, error) {
func (ms *metricsMiddleware) Attestation(ctx context.Context, reportData [agent.ReportDataSize]byte) ([]byte, error) {
defer func(begin time.Time) {
ms.counter.With("method", "attestation").Add(1)
ms.latency.With("method", "attestation").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.Attestation(ctx)
return ms.svc.Attestation(ctx, reportData)
}
+27 -26
View File
@@ -8,18 +8,27 @@ import (
"reflect"
)
var (
_ fmt.Stringer = (*Datasets)(nil)
_ fmt.Stringer = (*Algorithms)(nil)
)
var _ fmt.Stringer = (*Datasets)(nil)
type AgentConfig struct {
LogLevel string `json:"log_level"`
Host string `json:"host"`
Port string `json:"port"`
CertFile string `json:"cert_file"`
KeyFile string `json:"server_key"`
ServerCAFile string `json:"server_ca_file"`
ClientCAFile string `json:"client_ca_file"`
AttestedTls bool `json:"attested_tls"`
}
type Computation struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Datasets Datasets `json:"datasets,omitempty"`
Algorithms Algorithms `json:"algorithms,omitempty"`
ResultConsumers []string `json:"result_consumers,omitempty"`
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Datasets Datasets `json:"datasets,omitempty"`
Algorithm Algorithm `json:"algorithms,omitempty"`
ResultConsumers []string `json:"result_consumers,omitempty"`
AgentConfig AgentConfig `json:"agent_config,omitempty"`
}
func (d *Datasets) String() string {
@@ -30,30 +39,22 @@ func (d *Datasets) String() string {
return string(dat)
}
func (a *Algorithms) String() string {
dat, err := json.Marshal(a)
if err != nil {
return ""
}
return string(dat)
}
type Dataset struct {
Dataset []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
Dataset []byte `json:"-"`
Hash [32]byte `json:"hash,omitempty"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Datasets []Dataset
type Algorithm struct {
Algorithm []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
Algorithm []byte `json:"-"`
Hash [32]byte `json:"hash,omitempty"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Algorithms []Algorithm
func containsID(slice interface{}, id string) int {
rangeOnMe := reflect.ValueOf(slice)
for i := 0; i < rangeOnMe.Len(); i++ {
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package events
import (
"encoding/json"
"time"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
type service struct {
service string
computationID string
conn *vsock.Conn
}
type AgentEvent struct {
EventType string `json:"event_type"`
Timestamp time.Time `json:"timestamp"`
ComputationID string `json:"computation_id,omitempty"`
Details json.RawMessage `json:"details,omitempty"`
Originator string `json:"originator"`
Status string `json:"status,omitempty"`
}
type Service interface {
SendEvent(event, status string, details json.RawMessage) error
Close() error
}
func New(svc, computationID string, sockPort uint32) (Service, error) {
conn, err := vsock.Dial(vsock.Host, sockPort, nil)
if err != nil {
return nil, err
}
return &service{
service: svc,
computationID: computationID,
conn: conn,
}, nil
}
func (s *service) SendEvent(event, status string, details json.RawMessage) error {
body := manager.ClientStreamMessage{Message: &manager.ClientStreamMessage_AgentEvent{AgentEvent: &manager.AgentEvent{
EventType: event,
Timestamp: timestamppb.Now(),
ComputationId: s.computationID,
Originator: s.service,
Status: status,
Details: details,
}}}
protoBody, err := proto.Marshal(&body)
if err != nil {
return err
}
if _, err := s.conn.Write(protoBody); err != nil {
return err
}
return nil
}
func (s *service) Close() error {
return s.conn.Close()
}
+69 -78
View File
@@ -5,21 +5,24 @@ package agent
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log/slog"
"os/exec"
"slices"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/internal/events"
"github.com/google/go-sev-guest/client"
"github.com/ultravioletrs/cocos/agent/events"
"github.com/ultravioletrs/cocos/pkg/socket"
"golang.org/x/crypto/sha3"
)
var _ Service = (*agentService)(nil)
// ReportDataSize is the size of the report data expected by the attestation service.
const ReportDataSize = 64
var (
// ErrMalformedEntity indicates malformed entity specification (e.g.
// invalid username or password).
@@ -41,120 +44,110 @@ var (
errResultsNotReady = errors.New("computation results are not yet ready")
// errStateNotReady agent received a request in the wrong state.
errStateNotReady = errors.New("agent not expecting this operation in the current state")
// errHashMismatch provided algorithm/dataset does not match hash in manifest.
errHashMismatch = errors.New("malformed data, hash does not match manifest")
)
// Service specifies an API that must be fullfiled by the domain service
// implementation, and all of its decorators (e.g. logging & metrics).
type Service interface {
Run(ctx context.Context, c Computation) (string, error)
Algo(ctx context.Context, algorithm Algorithm) (string, error)
Data(ctx context.Context, dataset Dataset) (string, error)
Algo(ctx context.Context, algorithm Algorithm) error
Data(ctx context.Context, dataset Dataset) error
Result(ctx context.Context, consumer string) ([]byte, error)
Attestation(ctx context.Context) ([]byte, error)
Attestation(ctx context.Context, reportData [ReportDataSize]byte) ([]byte, error)
}
type agentService struct {
computation Computation
algorithms [][]byte
datasets [][]byte
result []byte
attestation []byte
sm *StateMachine
runError error
eventSvc events.Service
computation Computation // Holds the current computation request details.
algorithm []byte // Stores the algorithm received for the computation.
datasets [][]byte // Stores the datasets received for the computation.
result []byte // Stores the result of the computation.
sm *StateMachine // Manages the state transitions of the agent service.
runError error // Stores any error encountered during the computation run.
eventSvc events.Service // Service for publishing events related to computation.
}
const (
socketPath = "unix_socket"
pyRuntime = "python3"
notificationTopic = "agent"
socketPath = "unix_socket"
pyRuntime = "python3"
)
var _ Service = (*agentService)(nil)
// New instantiates the agent service implementation.
func New(ctx context.Context, logger mglog.Logger, eventSvc events.Service) Service {
func New(ctx context.Context, logger *slog.Logger, eventSvc events.Service, cmp Computation) Service {
svc := &agentService{
sm: NewStateMachine(logger),
eventSvc: eventSvc,
}
go svc.sm.Start(ctx)
svc.sm.SendEvent(start)
svc.sm.StateFunctions[idle] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[receivingManifests] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[receivingAlgorithms] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[receivingManifest] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[receivingAlgorithm] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[receivingData] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[resultsReady] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[complete] = svc.publishEvent("in-progress", json.RawMessage{})
svc.sm.StateFunctions[running] = svc.runComputation
svc.computation = cmp
svc.sm.SendEvent(manifestReceived)
return svc
}
func (as *agentService) Run(ctx context.Context, c Computation) (string, error) {
if as.sm.GetState() != receivingManifests {
return "", errStateNotReady
func (as *agentService) Algo(ctx context.Context, algorithm Algorithm) error {
if as.sm.GetState() != receivingAlgorithm {
return errStateNotReady
}
cmpJSON, err := json.Marshal(c)
if err != nil {
return "", err
if as.algorithm != nil {
return errAllManifestItemsReceived
}
as.computation = c
as.sm.SendEvent(manifestsReceived)
hash := sha3.Sum256(algorithm.Algorithm)
// Calculate the SHA-256 hash of the algorithm
hash := sha256.Sum256(cmpJSON)
cmpHash := hex.EncodeToString(hash[:])
return cmpHash, nil // return computation hash.
}
func (as *agentService) Algo(ctx context.Context, algorithm Algorithm) (string, error) {
if as.sm.GetState() != receivingAlgorithms {
return "", errStateNotReady
}
if len(as.computation.Algorithms) == 0 {
return "", errAllManifestItemsReceived
}
index := containsID(as.computation.Algorithms, algorithm.ID)
index := containsID(as.computation.Algorithm, algorithm.ID)
switch index {
case -1:
return "", errUndeclaredAlgorithm
return errUndeclaredAlgorithm
default:
if as.computation.Algorithms[index].Provider != algorithm.Provider {
return "", errProviderMissmatch
if as.computation.Algorithm.Provider != algorithm.Provider {
return errProviderMissmatch
}
if hash != as.computation.Algorithm.Hash {
return errHashMismatch
}
as.computation.Algorithms = slices.Delete(as.computation.Algorithms, index, index+1)
}
as.algorithms = append(as.algorithms, algorithm.Algorithm)
as.algorithm = algorithm.Algorithm
if len(as.computation.Algorithms) == 0 {
as.sm.SendEvent(algorithmsReceived)
if as.algorithm != nil {
as.sm.SendEvent(algorithmReceived)
}
// Calculate the SHA-256 hash of the algorithm.
hash := sha256.Sum256(algorithm.Algorithm)
algorithmHash := hex.EncodeToString(hash[:])
// Return the algorithm hash or an error.
return algorithmHash, nil
return nil
}
func (as *agentService) Data(ctx context.Context, dataset Dataset) (string, error) {
func (as *agentService) Data(ctx context.Context, dataset Dataset) error {
if as.sm.GetState() != receivingData {
return "", errStateNotReady
return errStateNotReady
}
if len(as.computation.Datasets) == 0 {
return "", errAllManifestItemsReceived
return errAllManifestItemsReceived
}
hash := sha3.Sum256(dataset.Dataset)
index := containsID(as.computation.Datasets, dataset.ID)
switch index {
case -1:
return "", errUndeclaredDataset
return errUndeclaredDataset
default:
if as.computation.Datasets[index].Provider != dataset.Provider {
return "", errProviderMissmatch
return errProviderMissmatch
}
if hash != as.computation.Datasets[index].Hash {
return errHashMismatch
}
as.computation.Datasets = slices.Delete(as.computation.Datasets, index, index+1)
}
@@ -165,12 +158,7 @@ func (as *agentService) Data(ctx context.Context, dataset Dataset) (string, erro
as.sm.SendEvent(dataReceived)
}
// Calculate the SHA-256 hash of the dataset.
hash := sha256.Sum256(dataset.Dataset)
datasetHash := hex.EncodeToString(hash[:])
// Return the dataset hash or an error.
return datasetHash, nil
return nil
}
func (as *agentService) Result(ctx context.Context, consumer string) ([]byte, error) {
@@ -195,14 +183,17 @@ func (as *agentService) Result(ctx context.Context, consumer string) ([]byte, er
return as.result, as.runError
}
func (as *agentService) Attestation(ctx context.Context) ([]byte, error) {
// Implement the logic for the Attestation method here
// Use the provided ctx parameter as needed
var attestation []byte
func (as *agentService) Attestation(ctx context.Context, reportData [ReportDataSize]byte) ([]byte, error) {
provider, err := client.GetQuoteProvider()
if err != nil {
return []byte{}, err
}
rawQuote, err := provider.GetRawQuote(reportData)
if err != nil {
return []byte{}, err
}
as.attestation = attestation
return as.attestation, nil
return rawQuote, nil
}
func (as *agentService) runComputation() {
@@ -210,7 +201,7 @@ func (as *agentService) runComputation() {
as.sm.logger.Debug("computation run started")
defer as.sm.SendEvent(runComplete)
as.publishEvent("in-progress", json.RawMessage{})()
result, err := run(as.algorithms[0], as.datasets[0])
result, err := run(as.algorithm, as.datasets[0])
if err != nil {
as.runError = err
as.publishEvent("failed", json.RawMessage{})()
@@ -222,7 +213,7 @@ func (as *agentService) runComputation() {
func (as *agentService) publishEvent(status string, details json.RawMessage) func() {
return func() {
if err := as.eventSvc.SendEvent(as.sm.State.String(), as.computation.ID, status, details); err != nil {
if err := as.eventSvc.SendEvent(as.sm.State.String(), status, details); err != nil {
as.sm.logger.Warn(err.Error())
}
}
+16 -13
View File
@@ -5,9 +5,8 @@ package agent
import (
"context"
"fmt"
"log/slog"
"sync"
mglog "github.com/absmach/magistrala/logger"
)
//go:generate stringer -type=state
@@ -15,8 +14,8 @@ type state int
const (
idle state = iota
receivingManifests
receivingAlgorithms
receivingManifest
receivingAlgorithm
receivingData
running
resultsReady
@@ -27,8 +26,8 @@ type event int
const (
start event = iota
manifestsReceived
algorithmsReceived
manifestReceived
algorithmReceived
dataReceived
runComplete
resultsConsumed
@@ -41,27 +40,29 @@ type StateMachine struct {
EventChan chan event
Transitions map[state]map[event]state
StateFunctions map[state]func()
logger mglog.Logger
logger *slog.Logger
wg *sync.WaitGroup
}
// NewStateMachine creates a new StateMachine.
func NewStateMachine(logger mglog.Logger) *StateMachine {
func NewStateMachine(logger *slog.Logger) *StateMachine {
sm := &StateMachine{
State: idle,
EventChan: make(chan event),
Transitions: make(map[state]map[event]state),
StateFunctions: make(map[state]func()),
logger: logger,
wg: &sync.WaitGroup{},
}
sm.Transitions[idle] = make(map[event]state)
sm.Transitions[idle][start] = receivingManifests
sm.Transitions[idle][start] = receivingManifest
sm.Transitions[receivingManifests] = make(map[event]state)
sm.Transitions[receivingManifests][manifestsReceived] = receivingAlgorithms
sm.Transitions[receivingManifest] = make(map[event]state)
sm.Transitions[receivingManifest][manifestReceived] = receivingAlgorithm
sm.Transitions[receivingAlgorithms] = make(map[event]state)
sm.Transitions[receivingAlgorithms][algorithmsReceived] = receivingData
sm.Transitions[receivingAlgorithm] = make(map[event]state)
sm.Transitions[receivingAlgorithm][algorithmReceived] = receivingData
sm.Transitions[receivingData] = make(map[event]state)
sm.Transitions[receivingData][dataReceived] = running
@@ -77,6 +78,8 @@ func NewStateMachine(logger mglog.Logger) *StateMachine {
// Start the state machine.
func (sm *StateMachine) Start(ctx context.Context) {
sm.wg.Add(1)
defer sm.wg.Done()
for {
select {
case event := <-sm.EventChan:
+2 -2
View File
@@ -9,8 +9,8 @@ func _() {
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[idle-0]
_ = x[receivingManifests-1]
_ = x[receivingAlgorithms-2]
_ = x[receivingManifest-1]
_ = x[receivingAlgorithm-2]
_ = x[receivingData-3]
_ = x[running-4]
_ = x[resultsReady-5]
+4 -6
View File
@@ -16,9 +16,9 @@ func TestStateMachineTransitions(t *testing.T) {
event event
expected state
}{
{idle, start, receivingManifests},
{receivingManifests, manifestsReceived, receivingAlgorithms},
{receivingAlgorithms, algorithmsReceived, receivingData},
{idle, start, receivingManifest},
{receivingManifest, manifestReceived, receivingAlgorithm},
{receivingAlgorithm, algorithmReceived, receivingData},
{receivingData, dataReceived, running},
{running, runComplete, resultsReady},
{resultsReady, resultsConsumed, complete},
@@ -27,12 +27,11 @@ func TestStateMachineTransitions(t *testing.T) {
for _, testCase := range testCases {
t.Run(fmt.Sprintf("Transition from %v to %v", testCase.fromState, testCase.expected), func(t *testing.T) {
sm := NewStateMachine(mglog.NewMock())
done := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func() {
sm.Start(ctx)
close(done)
}()
sm.wg.Wait()
sm.SetState(testCase.fromState)
sm.SendEvent(testCase.event)
@@ -42,7 +41,6 @@ func TestStateMachineTransitions(t *testing.T) {
}
close(sm.EventChan)
cancel()
<-done
})
}
}
-124
View File
@@ -1,124 +0,0 @@
openapi: 3.0.1
info:
title: CoCos-AI Manager
description: |
The manager service creates a VM with CoCos-AI agent:
- [The CoCos-AI repository](https://github.com/ultravioletrs/cocos-ai)
contact:
email: info@ultraviolet.com
license:
name: Apache 2.0
url: https://github.com/ultravioletrs/cocos-ai/blob/main/LICENCE.md
version: "1"
servers:
- url: http://localhost:9021
- url: https://localhost:9021
paths:
/run:
post:
summary: Run computation on VM.
description: |
Send computation to agent. This creates agent virtual machine and sends
computation manifest.
requestBody:
$ref: "#/components/requestBodies/Run"
responses:
"200":
description: Computation manifest created successfully.
"400":
description: Request has malformed content.
'500':
$ref: "#/components/responses/ServiceError"
/health:
get:
summary: Retrieves service health check info.
tags:
- health
responses:
'200':
$ref: "#/components/responses/HealthRes"
'500':
$ref: "#/components/responses/ServiceError"
components:
schemas:
HealthRes:
type: object
properties:
status:
type: string
description: Service status.
enum:
- pass
version:
type: string
description: Service version.
example: 0.0.1
commit:
type: string
description: Service commit hash.
example: 7d6f4dc4f7f0c1fa3dc24eddfb18bb5073ff4f62
description:
type: string
description: Service description.
example: <service_name> service
build_time:
type: string
description: Service build time.
example: 1970-01-01_00:00:00
requestBodies:
Run:
required: true
content:
application/json:
schema:
type: object
properties:
computation:
description: byte array of computation request as defined in ComputationReq
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
datasets:
type: array
items:
type: object
properties:
provider:
type: string
id:
type: string
algorithms:
type: array
items:
type: object
properties:
provider:
type: string
id:
type: string
results_consumers:
type: array
items:
type: string
timeout:
type: string
example: "2s"
responses:
ServiceError:
description: Unexpected server-side error occurred.
HealthRes:
description: Service Health Check.
content:
application/json:
schema:
$ref: "#/components/schemas/HealthRes"
+51 -18
View File
@@ -1,6 +1,6 @@
# Agent CLI
This repository contains the command-line interface (CLI) tool for interacting with the Agent service. The CLI allows you to perform various tasks such as running computations, uploading algorithms and datasets, and retrieving results.
This repository contains the command-line interface (CLI) tool for interacting with the Agent and manager service. The CLI allows you to perform various tasks such as running computations, uploading algorithms and datasets, and retrieving results.
## Build
@@ -17,15 +17,60 @@ make cli
To run a computation, use the following command:
```bash
./build/cocos-cli run --computation '{"name": "my-computation"}'
./build/cocos-cli manager run --computation '{"name": "my-computation"}'
```
#### Get attestation
Retrieves attestation information from the SEV guest and saves it to a file.
To retrieve attestation from agent, use the following command:
```bash
./build/cocos-cli agent attestation get '<report_data>'
```
#### Validate attestation
Validates the retrieved attestation information against a specified policy and checks its authenticity.
To validate and verify attestation from agent, use the following command:
```bash
./build/cocos-cli agent attestation validate '<attestation>' --report_data '<report_data>'
```
##### Flags
- --config: Path to a JSON file containing the validation configuration. This can be used to override individual flags.
- --report_data: Hex-encoded string representing the attestation report data. (Required for the validate command)
- --host_data: Hex-encoded string representing the expected HOST_DATA field.
- --family_id: Hex-encoded string representing the expected FAMILY_ID field.
- --image_id: Hex-encoded string representing the expected IMAGE_ID field.
- --report_id: Hex-encoded string representing the expected REPORT_ID field.
- --report_id_ma: Hex-encoded string representing the expected REPORT_ID_MA field.
- --measurement: Hex-encoded string representing the expected MEASUREMENT field.
- --chip_id: Hex-encoded string representing the expected CHIP_ID field.
- --minimum_tcb: Minimum acceptable value for CURRENT_TCB, COMMITTED_TCB, and REPORTED_TCB.
- --minimum_launch_tcb: Minimum acceptable value for LAUNCH_TCB.
- --minimum_guest_svn: Minimum acceptable SnpPolicy.
- --minimum_build: Minimum 8-bit build number for AMD-SP firmware.
- --check_crl: Download and check the CRL for revoked certificates (default: false).
- --disallow_network: If true, disallows network downloads for verification (default: false).
- --timeout: Duration to continue retrying failed HTTP requests (default: 2 minutes).
- --max_retry_delay: Maximum duration to wait between HTTP request retries (default: 30 seconds).
- --require_author_key: Require that AUTHOR_KEY_EN is set to 1 (default: false).
- --require_id_block: Require that the VM was launched with an ID_BLOCK signed by a trusted key (default: false).
- --permit_provisional_software: Allow provisional firmware (default: false).
- --platform_info: Maximum acceptable PLATFORM_INFO field bit-wise (optional).
- --minimum_version: Minimum AMD-SP firmware API version (major.minor) (optional).
- --trusted_author_keys: Paths to x.509 certificates of trusted author keys (optional).
- --trusted_author_key_hashes: Hex-encoded SHA-384 hashes of trusted author keys (optional).
- --trusted_id_keys: Paths to x.509 certificates of trusted identity keys (optional).
- --trusted_id_key_hashes: Hex-encoded SHA-384 hashes of trusted identity keys (optional).
- --product: AMD product name for the chip that generated the attestation report (optional).
- --stepping: Machine stepping for the chip that generated the attestation report (optional).
- --CA_bundles_paths: Paths to CA bundles for the AMD product (optional).
- --CA_bundles: PEM format CA bundles for the AMD product (optional).
#### Upload Algorithm
To upload an algorithm, use the following command:
```bash
./build/cocos-cli algo /path/to/algorithm
./build/cocos-cli agent algo /path/to/algorithm
```
#### Upload Dataset
@@ -33,7 +78,7 @@ To upload an algorithm, use the following command:
To upload a dataset, use the following command:
```bash
./build/cocos-cli data /path/to/dataset.csv
./build/cocos-cli agent data /path/to/dataset.csv
```
#### Retrieve result
@@ -41,17 +86,5 @@ To upload a dataset, use the following command:
To retrieve the computation result, use the following command:
```bash
./build/cocos-cli result
```
## Installtion
To use the CLI, you have the option to install it globally on your system. Here's how:
### Build the CLI:
Navigate to the project root and run the following command to build the CLI binary:
```bash
make install-cli
```
./build/cocos-cli agent result
```
+3 -4
View File
@@ -10,7 +10,7 @@ import (
"github.com/ultravioletrs/cocos/agent"
)
func NewAlgorithmsCmd(sdk agent.Service) *cobra.Command {
func (cli *CLI) NewAlgorithmsCmd() *cobra.Command {
return &cobra.Command{
Use: "algo",
Short: "Upload an algorithm binary",
@@ -32,12 +32,11 @@ func NewAlgorithmsCmd(sdk agent.Service) *cobra.Command {
Provider: args[2],
}
response, err := sdk.Algo(cmd.Context(), algoReq)
if err != nil {
if err := cli.agentSDK.Algo(cmd.Context(), algoReq); err != nil {
log.Fatalf("Error uploading algorithm with ID %s and provider %s: %v", algoReq.ID, algoReq.Provider, err)
}
log.Println("Successfully uploaded algorithm:", response)
log.Println("Successfully uploaded algorithm")
},
}
}
+605 -6
View File
@@ -3,23 +3,165 @@
package cli
import (
"encoding/hex"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/proto/check"
"github.com/google/go-sev-guest/validate"
"github.com/google/go-sev-guest/verify"
"github.com/google/go-sev-guest/verify/trust"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/ultravioletrs/cocos/agent"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const attestationFilePath = "attestation.txt"
const (
defaultMinimumTcb = 0
defaultMinimumLaunchTcb = 0
defaultMinimumGuestSvn = 0
defaultGuestPolicy = 0x0000000000030000
defaultMinimumBuild = 0
defaultCheckCrl = false
defaultTimeout = 2 * time.Minute
defaultMaxRetryDelay = 30 * time.Second
defaultRequireAuthor = false
defaultRequireIdBlock = false
defaultMinVersion = "0.0"
size16 = 16
size32 = 32
size48 = 48
size64 = 64
attestationFilePath = "attestation.bin"
exampleJSONConfig = `
{
"rootOfTrust":{
"product":"test_product",
"cabundlePaths":[
"test_cabundlePaths"
],
"cabundles":[
"test_Cabundles"
],
"checkCrl":true,
"disallowNetwork":true
},
"policy":{
"minimumGuestSvn":1,
"policy":"1",
"familyId":"AQIDBAUGBwgJCgsMDQ4PEA==",
"imageId":"AQIDBAUGBwgJCgsMDQ4PEA==",
"vmpl":0,
"minimumTcb":"1",
"minimumLaunchTcb":"1",
"platformInfo":"1",
"requireAuthorKey":true,
"reportData":"J+60aXs8btm8VcGgaJYURGeNCu0FIyWMFXQ7ZUlJDC0FJGJizJsOzDIXgQ75UtPC+Zqe0A3dvnnf5VEeQ61RTg==",
"measurement":"8s78ewoX7Xkfy1qsgVnkZwLDotD768Nqt6qTL5wtQOxHsLczipKM6bhDmWiHLdP4",
"hostData":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"reportId":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"reportIdMa":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"chipId":"J+60aXs8btm8VcGgaJYURGeNCu0FIyWMFXQ7ZUlJDC0FJGJizJsOzDIXgQ75UtPC+Zqe0A3dvnnf5VEeQ61RTg==",
"minimumBuild":1,
"minimumVersion":"0.90",
"permitProvisionalFirmware":true,
"requireIdBlock":true,
"trustedAuthorKeys":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedAuthorKeyHashes":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedIdKeys":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedIdKeyHashes":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"product":{
"name":"SEV_PRODUCT_MILAN",
"stepping":1,
"machineStepping":1
}
}
}
`
)
func NewAttestationCmd(sdk agent.Service) *cobra.Command {
var (
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
cfgString string
timeout time.Duration
maxRetryDelay time.Duration
platformInfo string
stepping string
trustedAuthorKeys []string
trustedAuthorHashes []string
trustedIdKeys []string
trustedIdKeyHashes []string
attestationFile string
attestation []byte
empty16 = [size16]byte{}
empty32 = [size32]byte{}
empty64 = [size64]byte{}
defaultReportIdMa = []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
)
func (cli *CLI) NewAttestationCmd() *cobra.Command {
return &cobra.Command{
Use: "attestation",
Short: "Retrieve attestation information",
Use: "attestation [command]",
Short: "Get and validate attestations",
Run: func(cmd *cobra.Command, args []string) {
log.Println("Checking attestation")
fmt.Printf("Get and validate attestations\n\n")
fmt.Printf("Usage:\n %s [command]\n\n", cmd.CommandPath())
fmt.Printf("Available Commands:\n")
result, err := sdk.Attestation(cmd.Context())
// Filter out "completion" command
availableCommands := make([]*cobra.Command, 0)
for _, subCmd := range cmd.Commands() {
if subCmd.Name() != "completion" {
availableCommands = append(availableCommands, subCmd)
}
}
for _, subCmd := range availableCommands {
fmt.Printf(" %-15s%s\n", subCmd.Name(), subCmd.Short)
}
fmt.Printf("\nFlags:\n")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
fmt.Printf(" -%s, --%s %s\n", flag.Shorthand, flag.Name, flag.Usage)
})
fmt.Printf("\nUse \"%s [command] --help\" for more information about a command.\n", cmd.CommandPath())
},
}
}
func (cli *CLI) NewGetAttestationCmd() *cobra.Command {
return &cobra.Command{
Use: "get",
Short: "Retrieve attestation information from agent. Report data expected in hex enoded string of length 64 bytes.",
Example: "report <report_data>",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
log.Println("Getting attestation")
reportData, err := hex.DecodeString(args[0])
if err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
if len(reportData) != agent.ReportDataSize {
log.Fatalf("report data must be a hex encoded string of length %d bytes", agent.ReportDataSize)
}
result, err := cli.agentSDK.Attestation(cmd.Context(), [agent.ReportDataSize]byte(reportData))
if err != nil {
log.Fatalf("Error retrieving attestation: %v", err)
}
@@ -32,3 +174,460 @@ func NewAttestationCmd(sdk agent.Service) *cobra.Command {
},
}
}
func (cli *CLI) NewValidateAttestationValidationCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "validate",
Short: "Validate and verify attestation information. The report is provided as a file path.",
Example: "validate <attestation_report_file_path>",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
log.Println("Checking attestation")
attestationFile = string(args[0])
if err := parseConfig(); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
if err := parseHashes(); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
if err := parseFiles(); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
// This format is the attestation report in AMD's specified ABI format, immediately
// followed by the certificate table bytes.
if len(attestation) < abi.ReportSize {
log.Fatalf("attestation contents too small (0x%x bytes). Want at least 0x%x bytes", len(attestation), abi.ReportSize)
}
if err := parseUints(); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
cfg.Policy.Vmpl = wrapperspb.UInt32(0)
if err := validateInput(); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
if err := verifyAndValidateAttestation(attestation); err != nil {
log.Fatalf("attestation validation and verification failed with error: %s", err)
}
log.Println("Attestation validation and verification is successful!")
},
}
cmd.Flags().StringVar(
&cfgString,
"config",
"",
"Serialized json check.Config protobuf. This will overwrite individual flags. Unmarshalled as json. Example: "+exampleJSONConfig,
)
cmd.Flags().BytesHexVar(
&cfg.Policy.HostData,
"host_data",
empty32[:],
"The expected HOST_DATA field as a hex string. Must encode 32 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.ReportData,
"report_data",
empty64[:],
"The expected REPORT_DATA field as a hex string. Must encode 64 bytes. Must be set.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.FamilyId,
"family_id",
empty16[:],
"The expected FAMILY_ID field as a hex string. Must encode 16 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.ImageId,
"image_id",
empty16[:],
"The expected IMAGE_ID field as a hex string. Must encode 16 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.ReportId,
"report_id",
nil,
"The expected REPORT_ID field as a hex string. Must encode 32 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.ReportIdMa,
"report_id_ma",
defaultReportIdMa,
"The expected REPORT_ID_MA field as a hex string. Must encode 32 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.Measurement,
"measurement",
nil,
"The expected MEASUREMENT field as a hex string. Must encode 48 bytes. Unchecked if unset.",
)
cmd.Flags().BytesHexVar(
&cfg.Policy.ChipId,
"chip_id",
nil,
"The expected MEASUREMENT field as a hex string. Must encode 48 bytes. Unchecked if unset.",
)
cmd.Flags().Uint64Var(
&cfg.Policy.MinimumTcb,
"minimum_tcb",
defaultMinimumTcb,
"The minimum acceptable value for CURRENT_TCB, COMMITTED_TCB, and REPORTED_TCB.",
)
cmd.Flags().Uint64Var(
&cfg.Policy.MinimumLaunchTcb,
"minimum_lauch_tcb",
defaultMinimumLaunchTcb,
"The minimum acceptable value for LAUNCH_TCB.",
)
cmd.Flags().Uint64Var(
&cfg.Policy.Policy,
"guest_policy",
defaultGuestPolicy,
"The most acceptable guest SnpPolicy.",
)
cmd.Flags().Uint32Var(
&cfg.Policy.MinimumGuestSvn,
"minimum_guest_svn",
defaultMinimumGuestSvn,
"The most acceptable GUEST_SVN.",
)
cmd.Flags().Uint32Var(
&cfg.Policy.MinimumBuild,
"minimum_build",
defaultMinimumBuild,
"The 8-bit minimum build number for AMD-SP firmware",
)
cmd.Flags().BoolVar(
&cfg.RootOfTrust.CheckCrl,
"check_crl",
defaultCheckCrl,
"Download and check the CRL for revoked certificates.",
)
cmd.Flags().DurationVar(
&timeout,
"timeout",
defaultTimeout,
"Duration to continue to retry failed HTTP requests.",
)
cmd.Flags().DurationVar(
&maxRetryDelay,
"max_retry_delay",
defaultMaxRetryDelay,
"Maximum Duration to wait between HTTP request retries.",
)
cmd.Flags().BoolVar(
&cfg.Policy.RequireAuthorKey,
"require_author_key",
defaultRequireAuthor,
"Require that AUTHOR_KEY_EN is 1.",
)
cmd.Flags().BoolVar(
&cfg.Policy.RequireIdBlock,
"require_id_block",
defaultRequireIdBlock,
"Require that the VM was launch with an ID_BLOCK signed by a trusted id key or author key",
)
cmd.Flags().StringVar(
&platformInfo,
"platform_info",
"",
"The maximum acceptable PLATFORM_INFO field bit-wise. May be empty or a 64-bit unsigned integer",
)
cmd.Flags().StringVar(
&cfg.Policy.MinimumVersion,
"minimum_version",
defaultMinVersion,
"Minimum AMD-SP firmware API version (major.minor). Each number must be 8-bit non-negative.",
)
cmd.Flags().StringArrayVar(
&trustedAuthorKeys,
"trusted_author_keys",
[]string{},
"Paths to x.509 certificates of trusted author keys",
)
cmd.Flags().StringArrayVar(
&trustedAuthorHashes,
"trusted_author_key_hashes",
[]string{},
"Hex-encoded SHA-384 hash values of trusted author keys in AMD public key format",
)
cmd.Flags().StringArrayVar(
&trustedIdKeys,
"trusted_id_keys",
[]string{},
"Paths to x.509 certificates of trusted author keys",
)
cmd.Flags().StringArrayVar(
&trustedIdKeyHashes,
"trusted_id_key_hashes",
[]string{},
"Hex-encoded SHA-384 hash values of trusted identity keys in AMD public key format",
)
cmd.Flags().StringVar(
&cfg.RootOfTrust.Product,
"product",
"",
"The AMD product name for the chip that generated the attestation report.",
)
cmd.Flags().StringVar(
&stepping,
"stepping",
"",
"The machine stepping for the chip that generated the attestation report. Default unchecked.",
)
cmd.Flags().StringArrayVar(
&cfg.RootOfTrust.CabundlePaths,
"CA_bundles_paths",
[]string{},
"Paths to CA bundles for the AMD product. Must be in PEM format, ASK, then ARK certificates. If unset, uses embedded root certificates.",
)
cmd.Flags().StringArrayVar(
&cfg.RootOfTrust.Cabundles,
"CA_bundles",
[]string{},
"PEM format CA bundles for the AMD product. Combined with contents of cabundle_paths.",
)
if err := cmd.MarkFlagRequired("report_data"); err != nil {
log.Fatalf("Failed to mark flag as required: %s", err)
}
return cmd
}
func verifyAndValidateAttestation(attestation []byte) error {
sopts, err := verify.RootOfTrustToOptions(cfg.RootOfTrust)
if err != nil {
return err
}
sopts.Product = cfg.Policy.Product
sopts.Getter = &trust.RetryHTTPSGetter{
Timeout: timeout,
MaxRetryDelay: maxRetryDelay,
Getter: &trust.SimpleHTTPSGetter{},
}
attestationPB, err := abi.ReportCertsToProto(attestation)
if err != nil {
return err
}
if err = verify.SnpAttestation(attestationPB, sopts); err != nil {
return err
}
opts, err := validate.PolicyToOptions(cfg.Policy)
if err != nil {
return err
}
if err = validate.SnpAttestation(attestationPB, opts); err != nil {
return err
}
return nil
}
// parseConfig decodes config passed as json for check.Config struct.
// example
/* {
"rootOfTrust":{
"product":"test_product",
"cabundlePaths":[
"test_cabundlePaths"
],
"cabundles":[
"test_Cabundles"
],
"checkCrl":true,
"disallowNetwork":true
},
"policy":{
"minimumGuestSvn":1,
"policy":"1",
"familyId":"AQIDBAUGBwgJCgsMDQ4PEA==",
"imageId":"AQIDBAUGBwgJCgsMDQ4PEA==",
"vmpl":0,
"minimumTcb":"1",
"minimumLaunchTcb":"1",
"platformInfo":"1",
"requireAuthorKey":true,
"reportData":"J+60aXs8btm8VcGgaJYURGeNCu0FIyWMFXQ7ZUlJDC0FJGJizJsOzDIXgQ75UtPC+Zqe0A3dvnnf5VEeQ61RTg==",
"measurement":"8s78ewoX7Xkfy1qsgVnkZwLDotD768Nqt6qTL5wtQOxHsLczipKM6bhDmWiHLdP4",
"hostData":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"reportId":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"reportIdMa":"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw=",
"chipId":"J+60aXs8btm8VcGgaJYURGeNCu0FIyWMFXQ7ZUlJDC0FJGJizJsOzDIXgQ75UtPC+Zqe0A3dvnnf5VEeQ61RTg==",
"minimumBuild":1,
"minimumVersion":"0.90",
"permitProvisionalFirmware":true,
"requireIdBlock":true,
"trustedAuthorKeys":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedAuthorKeyHashes":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedIdKeys":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"trustedIdKeyHashes":[
"GSvLKpfu59Y9QOF6vhq0vQsOIvb4+5O/UOHLGLBTkdw="
],
"product":{
"name":"SEV_PRODUCT_MILAN",
"stepping":1,
"machineStepping":1
}
}
}*/
func parseConfig() error {
if cfgString == "" {
return nil
}
if err := protojson.Unmarshal([]byte(cfgString), &cfg); err != nil {
return err
}
// Populate fields that should not be nil
if cfg.RootOfTrust == nil {
cfg.RootOfTrust = &check.RootOfTrust{}
}
if cfg.Policy == nil {
cfg.Policy = &check.Policy{}
}
return nil
}
func parseHashes() error {
for _, hash := range trustedAuthorHashes {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return err
}
cfg.Policy.TrustedAuthorKeyHashes = append(cfg.Policy.TrustedAuthorKeyHashes, hashBytes)
}
for _, hash := range trustedIdKeyHashes {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return err
}
cfg.Policy.TrustedIdKeyHashes = append(cfg.Policy.TrustedIdKeyHashes, hashBytes)
}
return nil
}
func parseFiles() error {
file, err := os.ReadFile(attestationFile)
if err != nil {
return err
}
attestation = file
for _, path := range trustedAuthorKeys {
file, err := os.ReadFile(path)
if err != nil {
return err
}
cfg.Policy.TrustedAuthorKeys = append(cfg.Policy.TrustedAuthorKeys, file)
}
for _, path := range trustedIdKeys {
file, err := os.ReadFile(path)
if err != nil {
return err
}
cfg.Policy.TrustedIdKeys = append(cfg.Policy.TrustedIdKeys, file)
}
return nil
}
func parseUints() error {
if stepping != "" {
if base := getBase(stepping); base == 10 {
num, err := strconv.ParseUint(stepping, getBase(stepping), 8)
if err != nil {
return err
}
cfg.Policy.Product.MachineStepping = wrapperspb.UInt32(uint32(num))
} else {
num, err := strconv.ParseUint(stepping[2:], base, 8)
if err != nil {
return err
}
cfg.Policy.Product.MachineStepping = wrapperspb.UInt32(uint32(num))
}
}
if platformInfo != "" {
if base := getBase(platformInfo); base == 10 {
num, err := strconv.ParseUint(platformInfo, getBase(platformInfo), 8)
if err != nil {
return err
}
cfg.Policy.PlatformInfo = wrapperspb.UInt64(num)
} else {
num, err := strconv.ParseUint(platformInfo[2:], base, 8)
if err != nil {
return err
}
cfg.Policy.PlatformInfo = wrapperspb.UInt64(num)
}
}
return nil
}
func getBase(val string) int {
switch {
case strings.HasPrefix(val, "0x"):
return 16
case strings.HasPrefix(val, "0o"):
return 8
case strings.HasPrefix(val, "0b"):
return 2
default:
return 10
}
}
func validateInput() error {
if len(cfg.RootOfTrust.CabundlePaths) != 0 || len(cfg.RootOfTrust.Cabundles) != 0 && cfg.RootOfTrust.Product == "" {
return fmt.Errorf("product name must be set if CA bundles are provided")
}
if err := validateFieldLength("report_data", cfg.Policy.ReportData, size64); err != nil {
return err
}
if err := validateFieldLength("host_data", cfg.Policy.HostData, size32); err != nil {
return err
}
if err := validateFieldLength("family_id", cfg.Policy.FamilyId, size16); err != nil {
return err
}
if err := validateFieldLength("image_id", cfg.Policy.ImageId, size16); err != nil {
return err
}
if err := validateFieldLength("report_id", cfg.Policy.ReportId, size32); err != nil {
return err
}
if err := validateFieldLength("report_id_ma", cfg.Policy.ReportIdMa, size32); err != nil {
return err
}
if err := validateFieldLength("measurement", cfg.Policy.Measurement, size48); err != nil {
return err
}
if err := validateFieldLength("chip_id", cfg.Policy.ChipId, size64); err != nil {
return err
}
for _, hash := range cfg.Policy.TrustedAuthorKeyHashes {
if err := validateFieldLength("trusted_author_key_hash", hash, size48); err != nil {
return err
}
}
for _, hash := range cfg.Policy.TrustedIdKeyHashes {
if err := validateFieldLength("trusted_id_key_hash", hash, size48); err != nil {
return err
}
}
return nil
}
func validateFieldLength(fieldName string, field []byte, expectedLength int) error {
if field != nil && len(field) != expectedLength {
return fmt.Errorf("%s length should be at least %d bytes long", fieldName, expectedLength)
}
return nil
}
+3 -4
View File
@@ -10,7 +10,7 @@ import (
"github.com/ultravioletrs/cocos/agent"
)
func NewDatasetsCmd(sdk agent.Service) *cobra.Command {
func (cli *CLI) NewDatasetsCmd() *cobra.Command {
return &cobra.Command{
Use: "data",
Short: "Upload a dataset CSV file",
@@ -32,12 +32,11 @@ func NewDatasetsCmd(sdk agent.Service) *cobra.Command {
Provider: args[2],
}
response, err := sdk.Data(cmd.Context(), dataReq)
if err != nil {
if err := cli.agentSDK.Data(cmd.Context(), dataReq); err != nil {
log.Fatalf("Error uploading dataset: %v", err)
}
log.Println("Response:", response)
log.Println("Successfully uploaded dataset")
},
}
}
-101
View File
@@ -1,101 +0,0 @@
openapi: 3.1.0
info:
title: Computation Service API
version: 1.0.0
servers:
- url: http://localhost:7002
paths:
/run:
post:
summary: Run a computation
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
description: The computation binary file (Linux executable)
responses:
"200":
description: Computation started
content:
application/json:
schema:
type: object
properties:
computationId:
type: string
description: Identifier for the computation
/algo:
post:
summary: Upload algorithm binary
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
description: The algorithm binary file (Linux executable)
responses:
"200":
description: Algorithm binary uploaded
content:
application/json:
schema:
type: object
properties:
algorithmId:
type: string
description: Identifier for the uploaded algorithm binary
/data:
post:
summary: Upload dataset CSV file
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
description: The dataset CSV file as a binary
responses:
"200":
description: Dataset CSV uploaded
content:
application/json:
schema:
type: object
properties:
datasetId:
type: string
description: Identifier for the uploaded dataset CSV
/result:
get:
summary: Retrieve computation result file
responses:
"200":
description: Computation result file retrieved successfully
content:
application/octet-stream:
schema:
type: string
format: binary
description: The computation result file
/attestation:
get:
summary: Attestation endpoint
responses:
"200":
description: Attestation result file retrieved successfully
content:
application/octet-stream:
schema:
type: string
format: binary
description: The attestation result file
+2 -3
View File
@@ -7,12 +7,11 @@ import (
"os"
"github.com/spf13/cobra"
"github.com/ultravioletrs/cocos/agent"
)
const resultFilePath = "result.bin"
func NewResultsCmd(sdk agent.Service) *cobra.Command {
func (cli *CLI) NewResultsCmd() *cobra.Command {
return &cobra.Command{
Use: "result",
Short: "Retrieve computation result file",
@@ -20,7 +19,7 @@ func NewResultsCmd(sdk agent.Service) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
log.Println("Retrieving computation result file")
result, err := sdk.Result(cmd.Context(), args[0])
result, err := cli.agentSDK.Result(cmd.Context(), args[0])
if err != nil {
log.Fatalf("Error retrieving computation result: %v", err)
}
+8 -1
View File
@@ -6,5 +6,12 @@ import (
"github.com/ultravioletrs/cocos/agent"
)
func SetSDK(s agent.Service) {
type CLI struct {
agentSDK agent.Service
}
func New(agentSDK agent.Service) *CLI {
return &CLI{
agentSDK: agentSDK,
}
}
+60 -71
View File
@@ -5,22 +5,20 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"strings"
"log/slog"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/agent/api"
agentgrpc "github.com/ultravioletrs/cocos/agent/api/grpc"
"github.com/ultravioletrs/cocos/agent/events"
"github.com/ultravioletrs/cocos/internal"
"github.com/ultravioletrs/cocos/internal/env"
"github.com/ultravioletrs/cocos/internal/events"
agentlogger "github.com/ultravioletrs/cocos/internal/logger"
"github.com/ultravioletrs/cocos/internal/server"
grpcserver "github.com/ultravioletrs/cocos/internal/server/grpc"
"github.com/ultravioletrs/cocos/manager"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
@@ -28,65 +26,54 @@ import (
const (
svcName = "agent"
envPrefixHTTP = "AGENT_HTTP_"
envPrefixGRPC = "AGENT_GRPC_"
defSvcHTTPPort = "9031"
defSvcGRPCPort = "7002"
)
var errComputationNotFound = errors.New("computation not found in command line")
type config struct {
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"`
InstanceID string `env:"AGENT_INSTANCE_ID" envDefault:""`
NotificationServerURL string `env:"COCOS_NOTIFICATION_SERVER_URL" envDefault:"http://localhost:9000"`
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
var cfg config
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err)
}
logger, err := mglog.New(os.Stdout, cfg.LogLevel)
cfg, err := readConfig()
if err != nil {
log.Fatalf(err.Error())
log.Fatalf("failed to read agent configuration from vsock %s", err.Error())
}
if cfg.InstanceID == "" {
cfg.InstanceID, err = uuid.New().ID()
if err != nil {
log.Fatalf("Failed to generate instanceID: %s", err)
}
}
eventSvc := events.New(svcName, cfg.NotificationServerURL)
svc := newService(ctx, logger, eventSvc)
ac, err := extractComputationValue()
conn, err := vsock.Dial(vsock.Host, manager.ManagerVsockPort, nil)
if err != nil {
logger.Fatal(fmt.Sprintf("computation not loaded from cmdline : %s", err.Error()))
log.Fatal(err)
}
if _, err := svc.Run(ctx, ac); err != nil {
if err := eventSvc.SendEvent("init", ac.ID, "failed", json.RawMessage{}); err != nil {
logger.Warn(err.Error())
}
logger.Fatal(fmt.Sprintf("failed to run computation with err: %s", err))
}
grpcServerConfig := server.Config{Port: defSvcGRPCPort}
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGRPC}); err != nil {
log.Printf("failed to load %s gRPC server configuration : %s", svcName, err.Error())
defer conn.Close()
var level slog.Level
if err := level.UnmarshalText([]byte(cfg.AgentConfig.LogLevel)); err != nil {
log.Println(err)
return
}
handler := agentlogger.NewProtoHandler(conn, &slog.HandlerOptions{Level: level})
logger := slog.New(handler)
eventSvc, err := events.New(svcName, cfg.ID, manager.ManagerVsockPort)
if err != nil {
log.Printf("failed to create events service %s", err.Error())
return
}
defer eventSvc.Close()
svc := newService(ctx, logger, eventSvc, cfg)
grpcServerConfig := server.Config{
Port: cfg.AgentConfig.Port,
Host: cfg.AgentConfig.Host,
CertFile: cfg.AgentConfig.CertFile,
KeyFile: cfg.AgentConfig.KeyFile,
ServerCAFile: cfg.AgentConfig.ServerCAFile,
ClientCAFile: cfg.AgentConfig.ClientCAFile,
AttestedTLS: cfg.AgentConfig.AttestedTls,
}
registerAgentServiceServer := func(srv *grpc.Server) {
reflection.Register(srv)
agent.RegisterAgentServiceServer(srv, agentgrpc.NewServer(svc))
}
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAgentServiceServer, logger)
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAgentServiceServer, logger, &svc)
g.Go(func() error {
return gs.Start()
@@ -101,8 +88,8 @@ func main() {
}
}
func newService(ctx context.Context, logger mglog.Logger, eventSvc events.Service) agent.Service {
svc := agent.New(ctx, logger, eventSvc)
func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Service, cmp agent.Computation) agent.Service {
svc := agent.New(ctx, logger, eventSvc, cmp)
svc = api.LoggingMiddleware(svc, logger)
counter, latency := internal.MakeMetrics(svcName, "api")
@@ -111,31 +98,33 @@ func newService(ctx context.Context, logger mglog.Logger, eventSvc events.Servic
return svc
}
// extractComputationValue to extract computation value from the command line.
func extractComputationValue() (agent.Computation, error) {
cmdLineBytes, err := os.ReadFile("/proc/cmdline")
func readConfig() (agent.Computation, error) {
l, err := vsock.Listen(manager.VsockConfigPort, nil)
if err != nil {
return agent.Computation{}, err
}
cmdLine := string(cmdLineBytes)
paramPrefix := "computation="
index := strings.Index(string(cmdLine), paramPrefix)
if index == -1 {
return agent.Computation{}, errComputationNotFound
}
start := index + len(paramPrefix)
end := strings.Index(cmdLine[start:], " ")
cmpUnescaped := cmdLine[start : start+end]
var ac agent.Computation
if end == -1 {
cmpUnescaped = cmdLine[start:]
}
if err := json.Unmarshal([]byte(cmpUnescaped), &ac); err != nil {
defer l.Close()
conn, err := l.Accept()
if err != nil {
return agent.Computation{}, err
}
defer conn.Close()
b := make([]byte, 1024)
n, err := conn.Read(b)
if err != nil {
return agent.Computation{}, err
}
ac := agent.Computation{
AgentConfig: agent.AgentConfig{},
}
if err := json.Unmarshal(b[:n], &ac); err != nil {
return agent.Computation{}, err
}
if ac.AgentConfig.LogLevel == "" {
ac.AgentConfig.LogLevel = "info"
}
if ac.AgentConfig.Port == "" {
ac.AgentConfig.Port = defSvcGRPCPort
}
return ac, nil
}
+20 -12
View File
@@ -20,10 +20,11 @@ import (
const (
svcName = "cli"
envPrefixAgentGRPC = "AGENT_GRPC_"
completion = "completion"
)
type config struct {
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"`
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"`
}
func main() {
@@ -39,31 +40,33 @@ func main() {
agentGRPCConfig := grpc.Config{}
if err := env.Parse(&agentGRPCConfig, env.Options{Prefix: envPrefixAgentGRPC}); err != nil {
logger.Fatal(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err))
logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err))
return
}
agentGRPCClient, agentClient, err := agent.NewAgentClient(agentGRPCConfig)
if err != nil {
logger.Fatal(err.Error())
logger.Error(err.Error())
return
}
defer agentGRPCClient.Close()
agentSDK := sdk.NewAgentSDK(logger, agentClient)
cli.SetSDK(agentSDK)
cliSVC := cli.New(agentSDK)
rootCmd := &cobra.Command{
Use: "cocos-cli [command]",
Short: "CLI application for Computation Service API",
Short: "CLI application for CoCos Service API",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("CLI application for Computation Service API\n\n")
fmt.Printf("CLI application for CoCos Service API\n\n")
fmt.Printf("Usage:\n %s [command]\n\n", cmd.CommandPath())
fmt.Printf("Available Commands:\n")
// Filter out "completion" command
availableCommands := make([]*cobra.Command, 0)
for _, subCmd := range cmd.Commands() {
if subCmd.Name() != "completion" {
if subCmd.Name() != completion {
availableCommands = append(availableCommands, subCmd)
}
}
@@ -80,11 +83,16 @@ func main() {
},
}
// Root Commands
rootCmd.AddCommand(cli.NewAlgorithmsCmd(agentSDK))
rootCmd.AddCommand(cli.NewDatasetsCmd(agentSDK))
rootCmd.AddCommand(cli.NewResultsCmd(agentSDK))
rootCmd.AddCommand(cli.NewAttestationCmd(agentSDK))
// Agent Commands
rootCmd.AddCommand(cliSVC.NewAlgorithmsCmd())
rootCmd.AddCommand(cliSVC.NewDatasetsCmd())
rootCmd.AddCommand(cliSVC.NewResultsCmd())
attestaionCmd := cliSVC.NewAttestationCmd()
rootCmd.AddCommand(attestaionCmd)
// Attestation commands
attestaionCmd.AddCommand(cliSVC.NewGetAttestationCmd())
attestaionCmd.AddCommand(cliSVC.NewValidateAttestationValidationCmd())
if err := rootCmd.Execute(); err != nil {
logger.Error(fmt.Sprintf("Command execution failed: %s", err))
+43 -53
View File
@@ -7,51 +7,41 @@ import (
"context"
"fmt"
"log"
"log/slog"
"os"
"strings"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/internal"
"github.com/ultravioletrs/cocos/internal/env"
"github.com/ultravioletrs/cocos/internal/events"
jaegerclient "github.com/ultravioletrs/cocos/internal/jaeger"
"github.com/ultravioletrs/cocos/internal/server"
grpcserver "github.com/ultravioletrs/cocos/internal/server/grpc"
httpserver "github.com/ultravioletrs/cocos/internal/server/http"
"github.com/ultravioletrs/cocos/manager"
"github.com/ultravioletrs/cocos/manager/api"
managergrpc "github.com/ultravioletrs/cocos/manager/api/grpc"
httpapi "github.com/ultravioletrs/cocos/manager/api/http"
managerapi "github.com/ultravioletrs/cocos/manager/api/grpc"
"github.com/ultravioletrs/cocos/manager/qemu"
"github.com/ultravioletrs/cocos/manager/tracing"
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
managergrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc/manager"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
"go.opentelemetry.io/otel/trace"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
svcName = "manager"
envPrefixHTTP = "MANAGER_HTTP_"
envPrefixGRPC = "MANAGER_GRPC_"
envPrefixAgentGRPC = "AGENT_GRPC_"
envPrefixQemu = "MANAGER_QEMU_"
defSvcGRPCPort = "7001"
defSvcHTTPPort = "9021"
svcName = "manager"
envPrefixGRPC = "MANAGER_GRPC_"
envPrefixQemu = "MANAGER_QEMU_"
)
type config struct {
LogLevel string `env:"MANAGER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"COCOS_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"`
InstanceID string `env:"MANAGER_INSTANCE_ID" envDefault:""`
NotificationServerURL string `env:"COCOS_NOTIFICATION_SERVER_URL" envDefault:"http://localhost:9000"`
HostIP string `env:"MANAGER_HOST_IP" envDefault:"localhost"`
LogLevel string `env:"MANAGER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"COCOS_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"`
InstanceID string `env:"MANAGER_INSTANCE_ID" envDefault:""`
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
ctx := context.Background()
g, ctx := errgroup.WithContext(ctx)
var cfg config
@@ -67,7 +57,8 @@ func main() {
if cfg.InstanceID == "" {
cfg.InstanceID, err = uuid.New().ID()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to generate instance ID: %s", err))
logger.Error(fmt.Sprintf("Failed to generate instance ID: %s", err))
return
}
}
@@ -84,43 +75,42 @@ func main() {
qemuCfg := qemu.Config{}
if err := env.Parse(&qemuCfg, env.Options{Prefix: envPrefixQemu}); err != nil {
logger.Fatal(fmt.Sprintf("failed to load QEMU configuration: %s", err))
logger.Error(fmt.Sprintf("failed to load QEMU configuration: %s", err))
return
}
exe, args, err := qemu.ExecutableAndArgs(qemuCfg, agent.Computation{})
exe, args, err := qemu.ExecutableAndArgs(qemuCfg)
if err != nil {
logger.Fatal(fmt.Sprintf("failed to parse QEMU configuration: %s", err))
logger.Error(fmt.Sprintf("failed to parse QEMU configuration: %s", err))
return
}
logger.Info(fmt.Sprintf("%s %s", exe, strings.Join(args, " ")))
svc := newService(logger, tracer, qemuCfg, events.New(svcName, cfg.NotificationServerURL), cfg)
httpServerConfig := server.Config{Port: defSvcHTTPPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil {
logger.Fatal(fmt.Sprintf("failed to load %s gRPC server configuration: %s", svcName, err))
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, httpapi.MakeHandler(svc, cfg.InstanceID), logger)
grpcServerConfig := server.Config{Port: defSvcGRPCPort}
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGRPC}); err != nil {
log.Printf("failed to load %s gRPC server configuration: %s", svcName, err.Error())
managerGRPCConfig := grpc.Config{}
if err := env.Parse(&managerGRPCConfig, env.Options{Prefix: envPrefixGRPC}); err != nil {
logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err))
return
}
registerManagerServiceServer := func(srv *grpc.Server) {
reflection.Register(srv)
manager.RegisterManagerServiceServer(srv, managergrpc.NewServer(svc))
managerGRPCClient, managerClient, err := managergrpc.NewManagerClient(managerGRPCConfig)
if err != nil {
logger.Error(err.Error())
return
}
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerManagerServiceServer, logger)
defer managerGRPCClient.Close()
pc, err := managerClient.Process(ctx)
if err != nil {
logger.Error(err.Error())
return
}
eventsChan := make(chan *pkgmanager.ClientStreamMessage)
svc := newService(logger, tracer, qemuCfg, eventsChan)
mc := managerapi.NewClient(pc, svc, eventsChan)
g.Go(func() error {
return hs.Start()
})
g.Go(func() error {
return gs.Start()
})
g.Go(func() error {
return server.StopHandler(ctx, cancel, logger, svcName, hs, gs)
return mc.Process(ctx)
})
if err := g.Wait(); err != nil {
@@ -133,9 +123,9 @@ func main() {
}
}
func newService(logger mglog.Logger, tracer trace.Tracer, qemuCfg qemu.Config, eventSvc events.Service, cfg config) manager.Service {
svc := manager.New(qemuCfg, logger, eventSvc, cfg.HostIP)
func newService(logger *slog.Logger, tracer trace.Tracer, qemuCfg qemu.Config, eventsChan chan *pkgmanager.ClientStreamMessage) manager.Service {
svc := manager.New(qemuCfg, logger, eventsChan)
go svc.RetrieveAgentEventsLogs()
svc = api.LoggingMiddleware(svc, logger)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
-21
View File
@@ -1,21 +0,0 @@
FROM golang:1.21-alpine AS builder
ARG SVC
ARG GOARCH
ARG GOARM
ARG VERSION
ARG COMMIT
ARG TIME
WORKDIR /go/src/github.com/ultravioletrs/cocos-ai
COPY . .
RUN apk update \
&& apk add make \
&& make $SVC \
&& mv build/cocos-$SVC /exe
FROM alpine:latest
RUN apk add qemu-system-x86_64
COPY --from=builder /exe /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["/exe"]
-7
View File
@@ -1,7 +0,0 @@
FROM alpine:latest
ARG SVC
RUN apk add --no-cache qemu-system-x86_64
COPY cocos-$SVC /exe
COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["/exe"]
-36
View File
@@ -1,36 +0,0 @@
version: "3.7"
name: "cocos"
networks:
cocos-base-net:
driver: bridge
services:
jaeger:
image: jaegertracing/all-in-one:1.38.0
container_name: cocos-jaeger
ports:
- ${COCOS_JAEGER_PORT}:${COCOS_JAEGER_PORT}/udp
- ${COCOS_JAEGER_FRONTEND}:${COCOS_JAEGER_FRONTEND}
- ${COCOS_JAEGER_COLLECTOR}:${COCOS_JAEGER_COLLECTOR}
- ${COCOS_JAEGER_CONFIGS}:${COCOS_JAEGER_CONFIGS}
networks:
- cocos-base-net
cocos-manager:
image: ghcr.io/ultravioletrs/cocos/manager:latest
container_name: cocos-manager
env_file:
- .env
devices:
- /dev/kvm
volumes:
- ../cmd/manager/img:/img
- /usr/share/edk2/x64/OVMF_CODE.fd:/usr/share/OVMF/OVMF_CODE.fd
- /usr/share/edk2/x64/OVMF_VARS.fd:/usr/share/OVMF/OVMF_VARS.fd
depends_on:
- jaeger
networks:
- cocos-base-net
ports:
- ${MANAGER_HTTP_PORT}:${MANAGER_HTTP_PORT}
- ${MANAGER_GRPC_PORT}:${MANAGER_GRPC_PORT}
+14 -7
View File
@@ -1,24 +1,26 @@
module github.com/ultravioletrs/cocos
go 1.21.3
go 1.21.6
require (
github.com/absmach/magistrala v0.0.0-20240110171157-9f573850fc4b
github.com/absmach/magistrala v0.0.0-20240119191055-d95283d31472
github.com/caarlos0/env/v7 v7.1.0
github.com/cenkalti/backoff/v4 v4.2.1
github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519
github.com/go-kit/kit v0.13.0
github.com/go-zoo/bone v1.3.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/google/go-sev-guest v0.10.1
github.com/mdlayher/vsock v1.2.1
github.com/prometheus/client_golang v1.18.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
golang.org/x/crypto v0.19.0
golang.org/x/sync v0.6.0
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.32.0
@@ -27,24 +29,29 @@ require (
require (
cloud.google.com/go/compute v1.23.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-configfs-tsm v0.2.2 // indirect
github.com/google/logger v1.1.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
+26 -10
View File
@@ -5,8 +5,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/absmach/magistrala v0.0.0-20240110171157-9f573850fc4b h1:5dwCCLImHebX52aVApF15ruZ/b0MB9Y4zECr3pmF0A4=
github.com/absmach/magistrala v0.0.0-20240110171157-9f573850fc4b/go.mod h1:7D+27TxXQwMqTmlerdulb0QtvXpMcSgeGoyvzhxr8MQ=
github.com/absmach/magistrala v0.0.0-20240119191055-d95283d31472 h1:MdS3yTTJP/OSFkMJ4+cJ0LnRS9OyPtPxsR0mSFYglXw=
github.com/absmach/magistrala v0.0.0-20240119191055-d95283d31472/go.mod h1:02Ip3Ow7JlPT4nb2TdZcOuf7C3ac8tzyEVwB+H1G40g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
@@ -26,8 +26,6 @@ github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519 h1:OpkN/n4
github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519/go.mod h1:WyJJyfmJ0gWJvjV+ZH4DOgtOYZc1KOvYyBXWCLKxsUU=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
@@ -39,8 +37,6 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-zoo/bone v1.3.0 h1:PY6sHq37FnQhj+4ZyqFIzJQHvrrGx0GEc3vTZZC/OsI=
github.com/go-zoo/bone v1.3.0/go.mod h1:HI3Lhb7G3UQcAwEhOJ2WyNcsFtQX1WYHa0Hl4OBbhW8=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -49,12 +45,29 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98=
github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
github.com/google/go-sev-guest v0.10.1 h1:kZIcGD513eqInbezqxchTtOLJlVXALyWlIXu5E0tNJE=
github.com/google/go-sev-guest v0.10.1/go.mod h1:/5hrgGWqG7+MPTXKhQz+v9ZE+Eh4MCBdItCNwgshlrk=
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -82,8 +95,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
@@ -98,8 +109,12 @@ go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -114,11 +129,12 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+1
View File
@@ -9,6 +9,7 @@ git clone git@github.com:ultravioletrs/cocos.git
git clone git@github.com:buildroot/buildroot.git
cd buildroot
make BR2_EXTERNAL=../cocos/hal/linux cocos_defconfig
# Execute 'make menuconfig' only if you want to make additional configuration changes to Buildroot.
make menuconfig
make
```
+2
View File
@@ -38,6 +38,8 @@ CONFIG_AUTOFS4_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_UNWINDER_FRAME_POINTER=y
CONFIG_VSOCKETS=y
CONFIG_VIRTIO_VSOCKETS=y
###
# AMD SEV-SNP
+3 -1
View File
@@ -4,7 +4,8 @@ BR2_x86_64=y
# System
BR2_TARGET_GENERIC_HOSTNAME="cocos"
BR2_TARGET_GENERIC_ISSUE="Welcome to Cocos"
BR2_SYSTEM_DHCP="eth0"
BR2_PACKAGE_DHCP=y
BR2_PACKAGE_DHCP_CLIENT=y
BR2_INIT_SYSTEMD=y
# Filesystem
@@ -44,3 +45,4 @@ BR2_PACKAGE_HOST_QEMU_SYSTEM_MODE=y
# Python
BR2_PACKAGE_PYTHON3=y
BR2_PACKAGE_PYTHON_PIP=y
+2 -2
View File
@@ -14,12 +14,12 @@ endef
define AGENT_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/build/cocos-agent $(TARGET_DIR)/bin
mkdir -p $(TARGET_DIR)/var/log/cocos
mkdir -p $(TARGET_DIR)/cocos/
endef
define AGENT_INSTALL_INIT_SYSTEMD
$(INSTALL) -D -m 0644 $(@D)/init/systemd/cocos-agent.service $(TARGET_DIR)/usr/lib/systemd/system/cocos-agent.service
$(INSTALL) -D -m 0644 $(@D)/init/systemd/eth0.network $(TARGET_DIR)/etc/systemd/network
$(INSTALL) -D -m 0644 $(@D)/init/systemd/00-network.link $(TARGET_DIR)/etc/systemd/network
$(INSTALL) -D -m 0755 $(@D)/init/systemd/agent_start_script.sh $(TARGET_DIR)/cocos/agent_start_script.sh
endef
$(eval $(golang-package))
-6
View File
@@ -1,6 +0,0 @@
[Match]
OriginalName=*
[Link]
NamePolicy=kernel
MACAddressPolicy=persistent
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
# The variable ETH_IFACE contains the name the systemd gave to the network interface.
# The systemd configures the name based on the QEMU parameters.
# The parts of the name enp0s2 mean:
# et - ethernet card. It means this is the ethernet interface.
# p - means that the interface is connected to a PCI bus.
# 0 - the interface is connected to bus 0.
# s2 -the interface is connected to slot 2.
# The variable ETH_IFACE value must match the name configured through QEMU parameters for the network device.
# The bus number and slot number are configured through QEMU device parameters, parameters
# addr (for slot number), and bus (for bus number).
ETH_IFACE=enp0s2
ip link set dev $ETH_IFACE up
dhclient $ETH_IFACE
AGENT_GRPC_HOST=$(ip -4 addr show $ETH_IFACE | grep inet | awk '{print $2}' | cut -d/ -f1)
export AGENT_GRPC_HOST
exec /bin/cocos-agent
+1 -2
View File
@@ -6,11 +6,10 @@ After=network.target
StandardOutput=file:/var/log/cocos/agent.stdout
StandardError=file:/var/log/cocos/agent.stderr
Environment=AGENT_GRPC_HOST=10.0.0.1
Environment=AGENT_GRPC_PORT=7002
Environment=AGENT_LOG_LEVEL=info
ExecStart=/bin/cocos-agent
ExecStart=/cocos/agent_start_script.sh
[Install]
WantedBy=default.target
-5
View File
@@ -1,5 +0,0 @@
[Match]
Name=eth0
[Network]
Address=10.0.0.1/24
+1 -2
View File
@@ -67,8 +67,7 @@ func RunCmdOutput(command string, args ...string) (string, error) {
func RunCmdStart(command string, args ...string) (*exec.Cmd, error) {
cmd := exec.Command(command, args...)
err := cmd.Start()
if err != nil {
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("error starting command '%s': %s", cmd.String(), err)
}
-11
View File
@@ -1,11 +0,0 @@
# Events package
## Overview
The events package provides a simple client for sending events to a server. It is designed to be used in conjunction with a server that can handle and process events.
`service`: A string representing the name of the service. Agent or manager.
`serverUrl`: A string representing the URL of the events server.
`event`: A string representing the event type of the events.
`computationId`: A string representing the computation ID associated with the events. This can be null for idle events.
The notifications are sent to endpoint `<server_url>/notifications/events` using post method.
-65
View File
@@ -1,65 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package events
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
var errFailedToCreateNotification = errors.New("failed to create notification on server")
type service struct {
service string
serverUrl string
}
type Service interface {
SendEvent(event, computationId, status string, details json.RawMessage) error
}
func New(svc, serverUrl string) Service {
return &service{
service: svc,
serverUrl: serverUrl,
}
}
func (s *service) SendEvent(event, computationId, status string, details json.RawMessage) error {
body := struct {
EventType string `json:"event_type"`
Timestamp time.Time `json:"timestamp"`
ComputationID string `json:"computation_id,omitempty"`
Details json.RawMessage `json:"details,omitempty"`
Originator string `json:"originator"`
Status string `json:"status,omitempty"`
}{
EventType: event,
Timestamp: time.Now(),
ComputationID: computationId,
Originator: s.service,
Status: status,
Details: details,
}
jsonBody, err := json.Marshal(body)
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/computations/events", s.serverUrl), bytes.NewReader(jsonBody))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != http.StatusCreated {
return errFailedToCreateNotification
}
return nil
}
+2 -2
View File
@@ -5,14 +5,14 @@ package libvirt
import (
"fmt"
"log"
"log/slog"
"net"
"time"
mglog "github.com/absmach/magistrala/logger"
"github.com/digitalocean/go-libvirt"
)
func Connect(logger mglog.Logger) *libvirt.Libvirt {
func Connect(logger *slog.Logger) *libvirt.Libvirt {
// This dials libvirt on the local machine, but you can substitute the first
// two parameters with "tcp", "<ip address>:<port>" to connect to libvirt on
// a remote machine.
+67
View File
@@ -0,0 +1,67 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package logger
import (
"context"
"io"
"log/slog"
"github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
var _ slog.Handler = (*handler)(nil)
type handler struct {
opts slog.HandlerOptions
w io.Writer
}
func NewProtoHandler(w io.Writer, opts *slog.HandlerOptions) slog.Handler {
if opts == nil {
opts = &slog.HandlerOptions{}
}
return &handler{
opts: *opts,
w: w,
}
}
// Enabled implements slog.Handler.
func (h *handler) Enabled(_ context.Context, l slog.Level) bool {
minLevel := slog.LevelInfo
if h.opts.Level != nil {
minLevel = h.opts.Level.Level()
}
return l >= minLevel
}
// Handle implements slog.Handler.
func (h *handler) Handle(_ context.Context, r slog.Record) error {
agentLog := manager.ClientStreamMessage{Message: &manager.ClientStreamMessage_AgentLog{AgentLog: &manager.AgentLog{
Timestamp: timestamppb.New(r.Time),
Message: r.Message,
Level: r.Level.String(),
}}}
b, err := proto.Marshal(&agentLog)
if err != nil {
return err
}
if _, err := h.w.Write(b); err != nil {
return err
}
return nil
}
// WithAttrs implements slog.Handler.
func (*handler) WithAttrs(attrs []slog.Attr) slog.Handler {
panic("unimplemented")
}
// WithGroup implements slog.Handler.
func (*handler) WithGroup(name string) slog.Handler {
panic("unimplemented")
}
+194 -14
View File
@@ -1,37 +1,60 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"log/slog"
"math/big"
"net"
"os"
"time"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/internal/server"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"golang.org/x/crypto/sha3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
const (
stopWaitTime = 5 * time.Second
stopWaitTime = 5 * time.Second
organization = "Ultraviolet"
country = "Serbia"
province = ""
locality = "Belgrade"
streetAddress = "Bulevar Arsenija Carnojevica 103"
postalCode = "11000"
notAfterYear = 1
notAfterMonth = 0
notAfterDay = 0
)
type Server struct {
server.BaseServer
server *grpc.Server
registerService serviceRegister
agent *agent.Service
}
type serviceRegister func(srv *grpc.Server)
var _ server.Server = (*Server)(nil)
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, registerService serviceRegister, logger mglog.Logger) server.Server {
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, registerService serviceRegister, logger *slog.Logger, agentSvc *agent.Service) server.Server {
listenFullAddress := fmt.Sprintf("%s:%s", config.Host, config.Port)
return &Server{
BaseServer: server.BaseServer{
Ctx: ctx,
@@ -42,35 +65,95 @@ func New(ctx context.Context, cancel context.CancelFunc, name string, config ser
Logger: logger,
},
registerService: registerService,
agent: agentSvc,
}
}
func (s *Server) Start() error {
errCh := make(chan error)
grpcServerOptions := []grpc.ServerOption{
grpc.StatsHandler(otelgrpc.NewServerHandler()),
}
listener, err := net.Listen("tcp", s.Address)
if err != nil {
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
}
creds := grpc.Creds(insecure.NewCredentials())
switch {
case s.Config.AttestedTLS:
certificateBytes, privateKeyBytes, err := generateCertificatesForATLS(s.agent)
if err != nil {
return fmt.Errorf("failed to create certificate: %w", err)
}
certificate, err := tls.X509KeyPair(certificateBytes, privateKeyBytes)
if err != nil {
return fmt.Errorf("falied due to invalid key pair: %w", err)
}
tlsConfig := &tls.Config{
ClientAuth: tls.NoClientCert,
Certificates: []tls.Certificate{certificate},
}
creds = grpc.Creds(credentials.NewTLS(tlsConfig))
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with Attested TLS", s.Name, s.Address))
case s.Config.CertFile != "" || s.Config.KeyFile != "":
creds, err := credentials.NewServerTLSFromFile(s.Config.CertFile, s.Config.KeyFile)
certificate, err := loadX509KeyPair(s.Config.CertFile, s.Config.KeyFile)
if err != nil {
return fmt.Errorf("failed to load auth certificates: %w", err)
}
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
s.server = grpc.NewServer(
grpc.Creds(creds),
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
)
tlsConfig := &tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{certificate},
}
var mtlsCA string
// Loading Server CA file
rootCA, err := loadCertFile(s.Config.ServerCAFile)
if err != nil {
return fmt.Errorf("failed to load root ca file: %w", err)
}
if len(rootCA) > 0 {
if tlsConfig.RootCAs == nil {
tlsConfig.RootCAs = x509.NewCertPool()
}
if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCA) {
return fmt.Errorf("failed to append root ca to tls.Config")
}
mtlsCA = fmt.Sprintf("root ca %s", s.Config.ServerCAFile)
}
// Loading Client CA File
clientCA, err := loadCertFile(s.Config.ClientCAFile)
if err != nil {
return fmt.Errorf("failed to load client ca file: %w", err)
}
if len(clientCA) > 0 {
if tlsConfig.ClientCAs == nil {
tlsConfig.ClientCAs = x509.NewCertPool()
}
if !tlsConfig.ClientCAs.AppendCertsFromPEM(clientCA) {
return fmt.Errorf("failed to append client ca to tls.Config")
}
mtlsCA = fmt.Sprintf("%s client ca %s", mtlsCA, s.Config.ClientCAFile)
}
creds = grpc.Creds(credentials.NewTLS(tlsConfig))
switch {
case mtlsCA != "":
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS/mTLS cert %s , key %s and %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile, mtlsCA))
default:
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
}
default:
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
s.server = grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
)
}
grpcServerOptions = append(grpcServerOptions, creds)
s.server = grpc.NewServer(grpcServerOptions...)
s.registerService(s.server)
go func() {
@@ -82,7 +165,6 @@ func (s *Server) Start() error {
return s.Stop()
case err := <-errCh:
s.Cancel()
return err
}
}
@@ -102,3 +184,101 @@ func (s *Server) Stop() error {
return nil
}
func loadCertFile(certFile string) ([]byte, error) {
if certFile != "" {
return os.ReadFile(certFile)
}
return []byte{}, nil
}
func loadX509KeyPair(certfile, keyfile string) (tls.Certificate, error) {
var cert, key []byte
var err error
if _, err = os.Stat(certfile); err == nil {
cert, err = os.ReadFile(certfile)
if err != nil {
return tls.Certificate{}, err
}
} else if os.IsNotExist(err) {
cert = []byte(certfile)
} else {
return tls.Certificate{}, err
}
if _, err := os.Stat(keyfile); err == nil {
key, err = os.ReadFile(keyfile)
if err != nil {
return tls.Certificate{}, err
}
} else if os.IsNotExist(err) {
key = []byte(keyfile)
} else {
return tls.Certificate{}, err
}
return tls.X509KeyPair(cert, key)
}
func generateCertificatesForATLS(svc *agent.Service) ([]byte, []byte, error) {
curve := elliptic.P256()
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate private/public key: %w", err)
}
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal the public key: %w", err)
}
// The Attestation Report will be added as an X.509 certificate extension
attestationReport, err := (*svc).Attestation(context.Background(), sha3.Sum512(publicKeyBytes))
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch the attestation report: %w", err)
}
certTemplate := &x509.Certificate{
SerialNumber: big.NewInt(202403311),
Subject: pkix.Name{
Organization: []string{organization},
Country: []string{country},
Province: []string{province},
Locality: []string{locality},
StreetAddress: []string{streetAddress},
PostalCode: []string{postalCode},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(notAfterYear, notAfterMonth, notAfterDay),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
ExtraExtensions: []pkix.Extension{
{
Id: asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6},
Critical: false,
Value: attestationReport,
},
},
}
certDERBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to create certificate: %w", err)
}
certBytes := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certDERBytes,
})
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal the private key: %w", err)
}
keyBytes := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: privateKeyBytes,
})
return certBytes, keyBytes, nil
}
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
// Package http contains the HTTP server implementation.
package http
-78
View File
@@ -1,78 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package http
import (
"context"
"fmt"
"net/http"
"time"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/internal/server"
)
const (
stopWaitTime = 5 * time.Second
httpProtocol = "http"
httpsProtocol = "https"
)
type Server struct {
server.BaseServer
server *http.Server
}
var _ server.Server = (*Server)(nil)
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, handler http.Handler, logger mglog.Logger) server.Server {
listenFullAddress := fmt.Sprintf("%s:%s", config.Host, config.Port)
httpServer := &http.Server{Addr: listenFullAddress, Handler: handler}
return &Server{
BaseServer: server.BaseServer{
Ctx: ctx,
Cancel: cancel,
Name: name,
Address: listenFullAddress,
Config: config,
Logger: logger,
},
server: httpServer,
}
}
func (s *Server) Start() error {
errCh := make(chan error)
s.Protocol = httpProtocol
switch {
case s.Config.CertFile != "" || s.Config.KeyFile != "":
s.Protocol = httpsProtocol
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile))
go func() {
errCh <- s.server.ListenAndServeTLS(s.Config.CertFile, s.Config.KeyFile)
}()
default:
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
go func() {
errCh <- s.server.ListenAndServe()
}()
}
select {
case <-s.Ctx.Done():
return s.Stop()
case err := <-errCh:
return err
}
}
func (s *Server) Stop() error {
defer s.Cancel()
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := s.server.Shutdown(ctxShutdown); err != nil {
s.Logger.Error(fmt.Sprintf("%s service %s server error occurred during shutdown at %s: %s", s.Name, s.Protocol, s.Address, err))
return fmt.Errorf("%s service %s server error occurred during shutdown at %s: %w", s.Name, s.Protocol, s.Address, err)
}
s.Logger.Info(fmt.Sprintf("%s %s service shutdown of http at %s", s.Name, s.Protocol, s.Address))
return nil
}
+10 -8
View File
@@ -5,11 +5,10 @@ package server
import (
"context"
"fmt"
"log/slog"
"os"
"os/signal"
"syscall"
mglog "github.com/absmach/magistrala/logger"
)
type Server interface {
@@ -18,10 +17,13 @@ type Server interface {
}
type Config struct {
Host string `env:"HOST" envDefault:""`
Port string `env:"PORT" envDefault:""`
CertFile string `env:"SERVER_CERT" envDefault:""`
KeyFile string `env:"SERVER_KEY" envDefault:""`
Host string `env:"HOST" envDefault:""`
Port string `env:"PORT" envDefault:""`
CertFile string `env:"SERVER_CERT" envDefault:""`
KeyFile string `env:"SERVER_KEY" envDefault:""`
ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""`
ClientCAFile string `env:"CLIENT_CA_CERTS" envDefault:""`
AttestedTLS bool `env:"ATTESTED_TLS" envDefault:"false"`
}
type BaseServer struct {
@@ -30,7 +32,7 @@ type BaseServer struct {
Name string
Address string
Config Config
Logger mglog.Logger
Logger *slog.Logger
Protocol string
}
@@ -49,7 +51,7 @@ func stopAllServer(servers ...Server) error {
return nil
}
func StopHandler(ctx context.Context, cancel context.CancelFunc, logger mglog.Logger, svcName string, servers ...Server) error {
func StopHandler(ctx context.Context, cancel context.CancelFunc, logger *slog.Logger, svcName string, servers ...Server) error {
var err error
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
+67 -80
View File
@@ -1,6 +1,6 @@
# Manager
Manager service provides a barebones HTTP and gRPC API and Service interface implementation for the development of the manager service.
Manager service provides a barebones gRPC API and Service interface implementation for the development of the manager service.
## Configuration
@@ -9,18 +9,14 @@ The service is configured using the environment variables from the following tab
| Variable | Description | Default |
| ----------------------------- | -------------------------------------------------------- | --------------------------------- |
| MANAGER_LOG_LEVEL | Log level for manager service (debug, info, warn, error) | info |
| MANAGER_HTTP_HOST | Manager service HTTP host | |
| MANAGER_HTTP_PORT | Manager service HTTP port | 9021 |
| MANAGER_HTTP_SERVER_CERT | Path to server certificate in pem format | |
| MANAGER_HTTP_SERVER_KEY | Path to server key in pem format | |
| MANAGER_GRPC_HOST | Manager service gRPC host | |
| MANAGER_GRPC_PORT | Manager service gRPC port | 7001 |
| MANAGER_GRPC_SERVER_CERT | Path to server certificate in pem format | |
| MANAGER_GRPC_SERVER_KEY | Path to server key in pem format | |
| MANAGER_GRPC_SERVER_CA_CERTS | Path to gRPC server CA certificate | |
| MANAGER_GRPC_CLIENT_CA_CERTS | Path to gRPC client CA certificate | |
| COCOS_JAEGER_URL | Jaeger server URL | http://localhost:14268/api/traces |
| MANAGER_INSTANCE_ID | Manager service instance ID | |
| COCOS_NOTIFICATION_SERVER_URL | Server to receive notification events from agent. | http:/localhost:9000 |
| MANAGER_HOST_IP | Mnagaer host IP address | localhost |
## Setup
@@ -47,6 +43,22 @@ sudo apt install qemu-kvm
Create `img` directory in `cmd/manager`. Create `tmp` directory in `cmd/manager`.
#### Add Vsock
The necessary kernel modules must be loaded on the hypervisor. To check if `vhost_vsock` is loaded run:
```shell
lsmod | grep vhost_vsock
```
If `vhost_vsock` is not loaded run the following commands:
```shell
sudo modprobe vhost_vsock
ls -l /dev/vhost-vsock
# crw-rw-rw- 1 root kvm 10, 241 Jan 16 12:05 /dev/vhost-vsock
ls -l /dev/vsock
# crw-rw-rw- 1 root root 10, 121 Jan 16 12:05 /dev/vsock
```
### Prepare Cocos HAL
Cocos HAL for Linux is framework for building custom in-enclave Linux distribution. Use the instructions in [Readme](https://github.com/ultravioletrs/cocos/blob/main/hal/linux/README.md).
@@ -76,10 +88,11 @@ qemu-system-x86_64 \
-m 2048M,slots=5,maxmem=10240M \
-no-reboot \
-drive if=pflash,format=raw,unit=0,file=$OVMF_CODE,readonly=on \
-netdev user,id=vmnic,hostfwd=tcp::2222-:22,hostfwd=tcp::9301-:9031,hostfwd=tcp::7020-:7002 \
-netdev user,id=vmnic,hostfwd=tcp::7020-:7002 \
-device virtio-net-pci,disable-legacy=on,iommu_platform=true,netdev=vmnic,romfile= \
-device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=3 -vnc :0 \
-kernel $KERNEL \
-append "earlyprintk=serial console=ttyS0 computation={\"id\":\"c0d15c5e-e37d-4426-b3b7-b432c966fb09\",\"name\":\"Sample_Computation\",\"description\":\"A_sample_computation\",\"datasets\":[{\"provider\":\"Provider1\",\"id\":\"Dataset1\"},{\"provider\":\"Provider2\",\"id\":\"Dataset2\"}],\"algorithms\":[{\"provider\":\"AlgorithmProvider1\",\"id\":\"Algorithm1\"}],\"result_consumers\":[\"Consumer1\"], \"timeout\":\"10m\"}" \
-append "earlyprintk=serial console=ttyS0" \
-initrd $INITRD \
-nographic \
-monitor pty \
@@ -88,10 +101,9 @@ qemu-system-x86_64 \
Once the VM is booted press enter and on the login use username `root`.
#### Build and run Agent
```sh
# Start the 'agent' executable in the background using '&' at the end.
cocos-agent &
Agent is started automatically in the VM.
```sh
# List running processes and use 'grep' to filter for processes containing 'agent' in their names.
ps aux | grep cocos-agent
# This command helps verify that the 'agent' process is running.
@@ -102,12 +114,6 @@ ps aux | grep cocos-agent
We can also check if `Agent` is reachable from the host machine:
```sh
# Use netcat (nc) to test the connection to localhost on port 9301.
nc -zv localhost 9301
# Output:
# nc: connect to localhost (::1) port 9301 (tcp) failed: Connection refused
# Connection to localhost (127.0.0.1) 9301 port [tcp/*] succeeded!
# Use netcat (nc) to test the connection to localhost on port 7020.
nc -zv localhost 7020
# Output:
@@ -138,23 +144,19 @@ NB: we set environment variables that we will use in the shell process where we
## Deployment
### Standalone
To start the service outside of the container, execute the following shell script:
To start the service, execute the following shell script (note a server needs to be running see [here](../test/computations/README.md)):
```bash
# download the latest version of the service
go get github.com/ultravioletrs/cocos
# Download the latest version of the service
git clone git@github.com:ultravioletrs/cocos.git
cd $GOPATH/src/github.com/ultravioletrs/cocos
cd cocos
# compile the manager
# Compile the manager
make manager
# copy binary to bin
make install
# set the environment variables and run the service
# Set the environment variables and run the service
MANAGER_GRPC_URL=localhost:7001
MANAGER_LOG_LEVEL=debug \
MANAGER_QEMU_USE_SUDO=false \
MANAGER_QEMU_ENABLE_SEV=false \
@@ -164,6 +166,7 @@ MANAGER_QEMU_ENABLE_SEV=false \
To enable [AMD SEV](https://www.amd.com/en/developer/sev.html) support, start manager like this
```sh
MANAGER_GRPC_URL=localhost:7001
MANAGER_LOG_LEVEL=debug \
MANAGER_QEMU_USE_SUDO=true \
MANAGER_QEMU_ENABLE_SEV=true \
@@ -171,66 +174,50 @@ MANAGER_QEMU_SEV_CBITPOS=51 \
./build/cocos-manager
```
The kernel hash feature might not work with the current build of OVMF and QEMU. If so, build the host kernel, QEMU, and OVMF from the [AMD SEV GitHub](https://github.com/AMDESE/AMDSEV/tree/snp-latest) repository.
### Docker
```bash
go get github.com/ultravioletrs/cocos
To build the OVMF with the kernel hash capability, we must build the AmdSev package of OVMF. The result of the build should be a single `OVMF.fd` file (unlike the regular two OVFM files). The OVMF package is located at `OvmfPkg/AmdSev/AmdSevX64.dsc`.
cd $GOPATH/src/github.com/ultravioletrs/cocos
# compile the manager
make manager
# create manager docker image
make docker_dev_manager
# start docker composition
make run
```
### Create QEMU virtual machine (VM)
To create an instance of VM and run a computation, run
To enable [AMD SEV-SNP](https://www.amd.com/en/developer/sev.html) support, start manager like this
```sh
curl -sSi -X POST \
http://localhost:9021/run \
-H "Content-Type: application/json" \
-d '{
"computation": {
"id":"c0d15c5e-e37d-4426-b3b7-b432c966fb09",
"name":"Sample_Computation",
"description":"A_sample_computation",
"datasets":[
{
"provider":"Provider1",
"id":"Dataset1"
},
{
"provider":"Provider2",
"id":"Dataset2"
}
],
"algorithms":[
{
"provider":"AlgorithmProvider1",
"id":"Algorithm1"
}
],
"result_consumers":[
"Consumer1"
],
"timeout":"10m"
}
}'
MANAGER_GRPC_URL=localhost:7001 \
MANAGER_LOG_LEVEL=debug \
MANAGER_QEMU_ENABLE_SEV=false \
MANAGER_QEMU_ENABLE_SEV_SNP=true \
MANAGER_QEMU_SEV_CBITPOS=51 \
MANAGER_QEMU_BIN_PATH=<path to QEMU binary> \
MANAGER_QEMU_QEMU_OVMF_CODE_FILE=<path to OVMF.fd Amd Sev built package> \
./build/cocos-manager
```
You should be able to create multiple instances by reruning the command.
To include the kernel hash into the measurement of the attestation report (SEV or SEV-SNP), start manager like this
```sh
MANAGER_GRPC_URL=localhost:7001 \
MANAGER_LOG_LEVEL=debug \
MANAGER_QEMU_ENABLE_SEV=false \
MANAGER_QEMU_ENABLE_SEV_SNP=true \
MANAGER_QEMU_SEV_CBITPOS=51 \
MANAGER_QEMU_KERNEL_HASH=true \
./build/cocos-manager
```
### Verifying VM launch
NB: To verify that the manager successfully launched the VM, you need to open two terminals on the same machine. In one terminal, you need to launch `go run main.go` (with the environment variables of choice) and in the other, you can run the verification commands.
NB: To verify that the manager successfully launched the VM, you need to open three terminals on the same machine. In one terminal, you need to launch the computations server by executing (with the environment variables of choice):
```bash
go run ./test/computations/main.go <dataset path> <algo path>
```
and in the second the manager by executing (with the environment variables of choice):
```bash
go run ./cmd/manager/main.go
```
Ensure that the Manager can connect to the Manager test server by setting the MANAGER_GRPC_PORT with the port value of the Manager test server. In the last terminal, you can run the verification commands.
To verify that the manager launched the VM successfully, run the following command:
+89
View File
@@ -0,0 +1,89 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package manager
import (
"fmt"
"net"
"regexp"
"strconv"
"github.com/absmach/magistrala/pkg/errors"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/proto"
)
const (
ManagerVsockPort = 9997
messageSize int = 1024
)
var errFailedToParseCID = errors.New("failed to parse cid from remote address")
// RetrieveAgentEventsLogs Retrieve and forward agent logs and events via vsock.
func (ms *managerService) RetrieveAgentEventsLogs() {
l, err := vsock.Listen(ManagerVsockPort, nil)
if err != nil {
ms.logger.Warn(err.Error())
return
}
for {
conn, err := l.Accept()
if err != nil {
ms.logger.Warn(err.Error())
continue
}
go ms.handleConnections(conn)
}
}
func (ms *managerService) handleConnections(conn net.Conn) {
defer conn.Close()
for {
b := make([]byte, messageSize)
n, err := conn.Read(b)
if err != nil {
ms.logger.Warn(err.Error())
return
}
cmpID, err := ms.computationIDFromAddress(conn.RemoteAddr().String())
if err != nil {
ms.logger.Warn(err.Error())
continue
}
var message manager.ClientStreamMessage
if err := proto.Unmarshal(b[:n], &message); err != nil {
ms.logger.Warn(err.Error())
continue
}
switch mes := message.Message.(type) {
case *manager.ClientStreamMessage_AgentEvent:
mes.AgentEvent.ComputationId = cmpID
ms.eventsChan <- &manager.ClientStreamMessage{Message: mes}
case *manager.ClientStreamMessage_AgentLog:
mes.AgentLog.ComputationId = cmpID
ms.eventsChan <- &manager.ClientStreamMessage{Message: mes}
default:
ms.logger.Warn("Unexpected agent log or event type")
}
ms.logger.Info(fmt.Sprintf("Agent Log/Event, Computation ID: %s, Message: %s", cmpID, message.String()))
}
}
func (ms *managerService) computationIDFromAddress(address string) (string, error) {
re := regexp.MustCompile(`vm\((\d+)\)`)
matches := re.FindStringSubmatch(address)
if len(matches) > 1 {
cid, err := strconv.Atoi(matches[1])
if err != nil {
return "", err
}
return ms.agents[cid], nil
}
return "", errFailedToParseCID
}
+41 -58
View File
@@ -4,72 +4,55 @@ package grpc
import (
"context"
"fmt"
"time"
"github.com/go-kit/kit/endpoint"
kitgrpc "github.com/go-kit/kit/transport/grpc"
"github.com/ultravioletrs/cocos/manager"
"google.golang.org/grpc"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
"golang.org/x/sync/errgroup"
)
const svcName = "manager.ManagerService"
type grpcClient struct {
run endpoint.Endpoint
timeout time.Duration
type ManagerClient struct {
stream pkgmanager.ManagerService_ProcessClient
svc manager.Service
responses chan *pkgmanager.ClientStreamMessage
}
// NewClient returns new gRPC client instance.
func NewClient(conn *grpc.ClientConn, timeout time.Duration) manager.ManagerServiceClient {
return &grpcClient{
run: kitgrpc.NewClient(
conn,
svcName,
"Run",
encodeRunRequest,
decodeRunResponse,
manager.RunResponse{},
).Endpoint(),
timeout: timeout,
func NewClient(stream pkgmanager.ManagerService_ProcessClient, svc manager.Service, responses chan *pkgmanager.ClientStreamMessage) ManagerClient {
return ManagerClient{
stream: stream,
svc: svc,
responses: responses,
}
}
// encodeRunRequest is a transport/grpc.EncodeRequestFunc that
// converts a user-domain runReq to a gRPC request.
func encodeRunRequest(_ context.Context, request interface{}) (interface{}, error) {
req, ok := request.(runReq)
if !ok {
return nil, fmt.Errorf("invalid request type: %T", request)
}
return &manager.RunRequest{
Computation: req.Computation,
}, nil
}
// decodeRunResponse is a transport/grpc.DecodeResponseFunc that
// converts a gRPC RunResponse to a user-domain response.
func decodeRunResponse(_ context.Context, grpcResponse interface{}) (interface{}, error) {
res, ok := grpcResponse.(*manager.RunResponse)
if !ok {
return nil, fmt.Errorf("invalid response type: %T", grpcResponse)
}
return runRes{AgentAddress: res.AgentAddress}, nil
}
func (client grpcClient) Run(ctx context.Context, req *manager.RunRequest, _ ...grpc.CallOption) (*manager.RunResponse, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
runReq := runReq{
Computation: req.GetComputation(),
}
res, err := client.run(ctx, runReq)
if err != nil {
return nil, err
}
runRes := res.(runRes)
return &manager.RunResponse{AgentAddress: runRes.AgentAddress}, nil
func (client ManagerClient) Process(ctx context.Context) error {
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
for {
req, err := client.stream.Recv()
if err != nil {
return err
}
port, err := client.svc.Run(ctx, req)
if err != nil {
return err
}
runRes := &pkgmanager.ClientStreamMessage_RunRes{RunRes: &pkgmanager.RunResponse{AgentPort: port, ComputationId: req.Id}}
if err := client.stream.Send(&pkgmanager.ClientStreamMessage{Message: runRes}); err != nil {
return err
}
}
})
eg.Go(func() error {
for mes := range client.responses {
if err := client.stream.Send(mes); err != nil {
return err
}
}
return nil
})
return eg.Wait()
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/ultravioletrs/cocos/manager"
)
func runEndpoint(svc manager.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(runReq)
if err := req.validate(); err != nil {
return runRes{}, err
}
agAddr, err := svc.Run(ctx, req.Computation)
if err != nil {
return runRes{}, err
}
return runRes{AgentAddress: agAddr}, nil
}
}
-15
View File
@@ -1,15 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"github.com/ultravioletrs/cocos/manager"
)
type runReq struct {
Computation *manager.Computation `json:"computation"`
}
func (req runReq) validate() error {
return nil
}
-7
View File
@@ -1,7 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package grpc
type runRes struct {
AgentAddress string `json:"agent_address"`
}
+41 -28
View File
@@ -5,44 +5,57 @@ package grpc
import (
"context"
"github.com/go-kit/kit/transport/grpc"
"github.com/ultravioletrs/cocos/manager"
"github.com/ultravioletrs/cocos/pkg/manager"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/peer"
)
type grpcServer struct {
run grpc.Handler
manager.UnimplementedManagerServiceServer
incoming chan *manager.ClientStreamMessage
svc Service
ctx context.Context
}
type Service interface {
Run(ipAddress string, runReqChan chan *manager.ComputationRunReq)
}
// NewServer returns new AuthServiceServer instance.
func NewServer(svc manager.Service) manager.ManagerServiceServer {
func NewServer(ctx context.Context, incoming chan *manager.ClientStreamMessage, svc Service) manager.ManagerServiceServer {
return &grpcServer{
run: grpc.NewServer(
runEndpoint(svc),
decodeRunRequest,
encodeRunResponse,
),
incoming: incoming,
svc: svc,
ctx: ctx,
}
}
func decodeRunRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*manager.RunRequest)
return runReq{
Computation: req.GetComputation(),
}, nil
}
func encodeRunResponse(_ context.Context, response interface{}) (interface{}, error) {
res := response.(runRes)
return &manager.RunResponse{AgentAddress: res.AgentAddress}, nil
}
func (s *grpcServer) Run(ctx context.Context, req *manager.RunRequest) (*manager.RunResponse, error) {
_, res, err := s.run.ServeGRPC(ctx, req)
if err != nil {
return nil, err
func (s *grpcServer) Process(stream manager.ManagerService_ProcessServer) error {
runReqChan := make(chan *manager.ComputationRunReq)
client, ok := peer.FromContext(stream.Context())
if ok {
go s.svc.Run(client.Addr.String(), runReqChan)
}
rr := res.(*manager.RunResponse)
return rr, nil
eg, _ := errgroup.WithContext(s.ctx)
eg.Go(func() error {
for {
req, err := stream.Recv()
if err != nil {
return err
}
s.incoming <- req
}
})
eg.Go(func() error {
for runReq := range runReqChan {
if err := stream.Send(runReq); err != nil {
return err
}
}
return nil
})
return eg.Wait()
}
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
// Package http contains implementation of kit service HTTP API.
package http
-41
View File
@@ -1,41 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package http
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/ultravioletrs/cocos/manager"
)
func runEndpoint(svc manager.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(runReq)
if err := req.validate(); err != nil {
return nil, err
}
mc := manager.Computation{
Id: req.Computation.ID,
Name: req.Computation.Name,
Description: req.Computation.Description,
ResultConsumers: req.Computation.ResultConsumers,
}
for _, algo := range req.Computation.Algorithms {
mc.Algorithms = append(mc.Algorithms, &manager.Algorithm{Id: algo.ID, Provider: algo.Provider})
}
for _, data := range req.Computation.Datasets {
mc.Datasets = append(mc.Datasets, &manager.Dataset{Id: data.ID, Provider: data.Provider})
}
// Call the Run method on the service
agAddr, err := svc.Run(ctx, &mc)
if err != nil {
return nil, err
}
return runRes{AgentAddress: agAddr}, nil
}
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package http
import (
"github.com/ultravioletrs/cocos/agent"
)
var _ apiReq = (*runReq)(nil)
type apiReq interface {
validate() error
}
type runReq struct {
Computation agent.Computation `json:"computation"`
}
func (req *runReq) validate() error {
return nil
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package http
import (
"net/http"
"github.com/absmach/magistrala"
)
var _ magistrala.Response = (*runRes)(nil)
type runRes struct {
AgentAddress string `json:"agent_address"`
}
func (res runRes) Code() int {
return http.StatusOK
}
func (res runRes) Headers() map[string]string {
return map[string]string{}
}
func (res runRes) Empty() bool {
return true
}
-108
View File
@@ -1,108 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package http
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"github.com/absmach/magistrala"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/go-zoo/bone"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/ultravioletrs/cocos/manager"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
const contentType = "application/json"
var (
errUnsupportedContentType = errors.New("unsupported content type")
errInvalidQueryParams = errors.New("invalid query params")
)
// MakeHandler returns a HTTP handler for API endpoints.
func MakeHandler(svc manager.Service, instanceID string) http.Handler {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(encodeError),
}
r := bone.New()
r.Post("/run", otelhttp.NewHandler(kithttp.NewServer(
runEndpoint(svc),
decodeRun,
encodeResponse,
opts...,
), "run"))
r.GetFunc("/health", magistrala.Health("manager", instanceID))
r.Handle("/metrics", promhttp.Handler())
return r
}
func decodeRun(_ context.Context, r *http.Request) (interface{}, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errUnsupportedContentType
}
var req runReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", contentType)
if ar, ok := response.(magistrala.Response); ok {
for k, v := range ar.Headers() {
w.Header().Set(k, v)
}
w.WriteHeader(ar.Code())
if ar.Empty() {
return nil
}
}
return json.NewEncoder(w).Encode(response)
}
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", contentType)
switch err {
case manager.ErrMalformedEntity:
w.WriteHeader(http.StatusBadRequest)
case manager.ErrNotFound:
w.WriteHeader(http.StatusNotFound)
case manager.ErrUnauthorizedAccess:
w.WriteHeader(http.StatusForbidden)
case errUnsupportedContentType:
w.WriteHeader(http.StatusUnsupportedMediaType)
case errInvalidQueryParams:
w.WriteHeader(http.StatusBadRequest)
case io.ErrUnexpectedEOF:
w.WriteHeader(http.StatusBadRequest)
case io.EOF:
w.WriteHeader(http.StatusBadRequest)
default:
switch err.(type) {
case *json.SyntaxError:
w.WriteHeader(http.StatusBadRequest)
case *json.UnmarshalTypeError:
w.WriteHeader(http.StatusBadRequest)
default:
w.WriteHeader(http.StatusInternalServerError)
}
}
}
+9 -4
View File
@@ -9,25 +9,26 @@ package api
import (
"context"
"fmt"
"log/slog"
"time"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/manager"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
)
var _ manager.Service = (*loggingMiddleware)(nil)
type loggingMiddleware struct {
logger mglog.Logger
logger *slog.Logger
svc manager.Service
}
// LoggingMiddleware adds logging facilities to the core service.
func LoggingMiddleware(svc manager.Service, logger mglog.Logger) manager.Service {
func LoggingMiddleware(svc manager.Service, logger *slog.Logger) manager.Service {
return &loggingMiddleware{logger, svc}
}
func (lm *loggingMiddleware) Run(ctx context.Context, mc *manager.Computation) (agentAddr string, err error) {
func (lm *loggingMiddleware) Run(ctx context.Context, mc *pkgmanager.ComputationRunReq) (agentAddr string, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method Run for computation took %s to complete", time.Since(begin))
if err != nil {
@@ -39,3 +40,7 @@ func (lm *loggingMiddleware) Run(ctx context.Context, mc *manager.Computation) (
return lm.svc.Run(ctx, mc)
}
func (lm *loggingMiddleware) RetrieveAgentEventsLogs() {
lm.svc.RetrieveAgentEventsLogs()
}
+6 -1
View File
@@ -12,6 +12,7 @@ import (
"github.com/go-kit/kit/metrics"
"github.com/ultravioletrs/cocos/manager"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
)
var _ manager.Service = (*metricsMiddleware)(nil)
@@ -32,7 +33,7 @@ func MetricsMiddleware(svc manager.Service, counter metrics.Counter, latency met
}
}
func (ms *metricsMiddleware) Run(ctx context.Context, mc *manager.Computation) (string, error) {
func (ms *metricsMiddleware) Run(ctx context.Context, mc *pkgmanager.ComputationRunReq) (string, error) {
defer func(begin time.Time) {
ms.counter.With("method", "Run").Add(1)
ms.latency.With("method", "Run").Observe(time.Since(begin).Seconds())
@@ -40,3 +41,7 @@ func (ms *metricsMiddleware) Run(ctx context.Context, mc *manager.Computation) (
return ms.svc.Run(ctx, mc)
}
func (ms *metricsMiddleware) RetrieveAgentEventsLogs() {
ms.svc.RetrieveAgentEventsLogs()
}
-477
View File
@@ -1,477 +0,0 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v4.25.1
// source: manager/manager.proto
package manager
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RunRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Computation *Computation `protobuf:"bytes,1,opt,name=computation,proto3" json:"computation,omitempty"`
}
func (x *RunRequest) Reset() {
*x = RunRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RunRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RunRequest) ProtoMessage() {}
func (x *RunRequest) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RunRequest.ProtoReflect.Descriptor instead.
func (*RunRequest) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{0}
}
func (x *RunRequest) GetComputation() *Computation {
if x != nil {
return x.Computation
}
return nil
}
type Computation struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Datasets []*Dataset `protobuf:"bytes,4,rep,name=datasets,proto3" json:"datasets,omitempty"`
Algorithms []*Algorithm `protobuf:"bytes,5,rep,name=algorithms,proto3" json:"algorithms,omitempty"`
ResultConsumers []string `protobuf:"bytes,6,rep,name=result_consumers,json=resultConsumers,proto3" json:"result_consumers,omitempty"`
}
func (x *Computation) Reset() {
*x = Computation{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Computation) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Computation) ProtoMessage() {}
func (x *Computation) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Computation.ProtoReflect.Descriptor instead.
func (*Computation) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{1}
}
func (x *Computation) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Computation) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Computation) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *Computation) GetDatasets() []*Dataset {
if x != nil {
return x.Datasets
}
return nil
}
func (x *Computation) GetAlgorithms() []*Algorithm {
if x != nil {
return x.Algorithms
}
return nil
}
func (x *Computation) GetResultConsumers() []string {
if x != nil {
return x.ResultConsumers
}
return nil
}
type Dataset struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *Dataset) Reset() {
*x = Dataset{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Dataset) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Dataset) ProtoMessage() {}
func (x *Dataset) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Dataset.ProtoReflect.Descriptor instead.
func (*Dataset) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{2}
}
func (x *Dataset) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *Dataset) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type Algorithm struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *Algorithm) Reset() {
*x = Algorithm{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Algorithm) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Algorithm) ProtoMessage() {}
func (x *Algorithm) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Algorithm.ProtoReflect.Descriptor instead.
func (*Algorithm) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{3}
}
func (x *Algorithm) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *Algorithm) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type RunResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AgentAddress string `protobuf:"bytes,1,opt,name=agent_address,json=agentAddress,proto3" json:"agent_address,omitempty"`
}
func (x *RunResponse) Reset() {
*x = RunResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RunResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RunResponse) ProtoMessage() {}
func (x *RunResponse) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RunResponse.ProtoReflect.Descriptor instead.
func (*RunResponse) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{4}
}
func (x *RunResponse) GetAgentAddress() string {
if x != nil {
return x.AgentAddress
}
return ""
}
var File_manager_manager_proto protoreflect.FileDescriptor
var file_manager_manager_proto_rawDesc = []byte{
0x0a, 0x15, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
0x22, 0x44, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36,
0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x6f,
0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x75,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xe0, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x75,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x08,
0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74,
0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x61, 0x6c,
0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
0x68, 0x6d, 0x52, 0x0a, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x12, 0x29,
0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x35, 0x0a, 0x07, 0x44, 0x61, 0x74,
0x61, 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x22, 0x37, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x32, 0x0a, 0x0b, 0x52, 0x75, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e,
0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x32, 0x44, 0x0a,
0x0e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x32, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x13, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_manager_manager_proto_rawDescOnce sync.Once
file_manager_manager_proto_rawDescData = file_manager_manager_proto_rawDesc
)
func file_manager_manager_proto_rawDescGZIP() []byte {
file_manager_manager_proto_rawDescOnce.Do(func() {
file_manager_manager_proto_rawDescData = protoimpl.X.CompressGZIP(file_manager_manager_proto_rawDescData)
})
return file_manager_manager_proto_rawDescData
}
var file_manager_manager_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_manager_manager_proto_goTypes = []interface{}{
(*RunRequest)(nil), // 0: manager.RunRequest
(*Computation)(nil), // 1: manager.Computation
(*Dataset)(nil), // 2: manager.Dataset
(*Algorithm)(nil), // 3: manager.Algorithm
(*RunResponse)(nil), // 4: manager.RunResponse
}
var file_manager_manager_proto_depIdxs = []int32{
1, // 0: manager.RunRequest.computation:type_name -> manager.Computation
2, // 1: manager.Computation.datasets:type_name -> manager.Dataset
3, // 2: manager.Computation.algorithms:type_name -> manager.Algorithm
0, // 3: manager.ManagerService.Run:input_type -> manager.RunRequest
4, // 4: manager.ManagerService.Run:output_type -> manager.RunResponse
4, // [4:5] is the sub-list for method output_type
3, // [3:4] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_manager_manager_proto_init() }
func file_manager_manager_proto_init() {
if File_manager_manager_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_manager_manager_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RunRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Computation); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Dataset); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Algorithm); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RunResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_manager_manager_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_manager_manager_proto_goTypes,
DependencyIndexes: file_manager_manager_proto_depIdxs,
MessageInfos: file_manager_manager_proto_msgTypes,
}.Build()
File_manager_manager_proto = out.File
file_manager_manager_proto_rawDesc = nil
file_manager_manager_proto_goTypes = nil
file_manager_manager_proto_depIdxs = nil
}
+44 -7
View File
@@ -3,37 +3,74 @@
syntax = "proto3";
import "google/protobuf/timestamp.proto";
package manager;
option go_package = "./manager";
service ManagerService {
rpc Run(RunRequest) returns (RunResponse) {}
rpc Process(stream ClientStreamMessage) returns (stream ComputationRunReq) {}
}
message RunRequest {
Computation computation = 1;
message RunResponse{
string agent_port = 1;
string computation_id = 2;
}
message Computation {
message AgentEvent {
string event_type = 1;
google.protobuf.Timestamp timestamp = 2;
string computation_id = 3;
bytes details = 4;
string originator = 5;
string status = 6;
}
message AgentLog {
string message = 1;
string computation_id = 2;
string level = 3;
google.protobuf.Timestamp timestamp = 4;
}
message ClientStreamMessage {
oneof message {
AgentLog agent_log = 1;
AgentEvent agent_event = 2;
RunResponse run_res = 3;
}
}
message ComputationRunReq {
string id = 1;
string name = 2;
string description = 3;
repeated Dataset datasets = 4;
repeated Algorithm algorithms = 5;
Algorithm algorithm = 5;
repeated string result_consumers = 6;
AgentConfig agent_config = 7;
}
message Dataset {
string provider = 1;
string id = 2;
bytes hash = 3; // should be sha3.Sum256, 32 byte length.
}
message Algorithm {
string provider = 1;
string id = 2;
bytes hash = 3; // should be sha3.Sum256, 32 byte length.
}
message RunResponse {
string agent_address = 1;
message AgentConfig {
string port = 1;
string host = 2;
string cert_file = 3;
string key_file = 4;
string client_ca_file = 5;
string server_ca_file = 6;
string log_level = 7;
bool attested_tls = 8;
}
+68 -38
View File
@@ -4,6 +4,7 @@ package qemu
import (
"fmt"
"strconv"
)
type MemoryConfig struct {
@@ -36,6 +37,7 @@ type NetDevConfig struct {
type VirtioNetPciConfig struct {
DisableLegacy string `env:"VIRTIO_NET_PCI_DISABLE_LEGACY" envDefault:"on"`
IOMMUPlatform bool `env:"VIRTIO_NET_PCI_IOMMU_PLATFORM" envDefault:"true"`
Addr string `env:"VIRTIO_NET_PCI_ADDR" envDefault:"0x2"`
ROMFile string `env:"VIRTIO_NET_PCI_ROMFILE"`
}
@@ -44,32 +46,38 @@ type DiskImgConfig struct {
RootFsFile string `env:"DISK_IMG_ROOTFS_FILE" envDefault:"img/rootfs.cpio.gz"`
}
type VirtioScsiPciConfig struct {
ID string `env:"VIRTIO_SCSI_PCI_ID" envDefault:"scsi"`
DisableLegacy string `env:"VIRTIO_SCSI_PCI_DISABLE_LEGACY" envDefault:"on"`
IOMMUPlatform bool `env:"VIRTIO_SCSI_PCI_IOMMU_PLATFORM" envDefault:"true"`
}
type SevConfig struct {
ID string `env:"SEV_ID" envDefault:"sev0"`
CBitPos int `env:"SEV_CBITPOS" envDefault:"51"`
ReducedPhysBits int `env:"SEV_REDUCED_PHYS_BITS" envDefault:"1"`
}
type VSockConfig struct {
ID string `env:"VSOCK_ID" envDefault:"vhost-vsock-pci0"`
GuestCID int `env:"VSOCK_GUEST_CID" envDefault:"3"`
vnc int `env:"VSOCK_VNC" envDefault:"0"`
}
type Config struct {
TmpFileLoc string `env:"TMP_FILE_LOC" envDefault:"tmp"`
UseSudo bool `env:"USE_SUDO" envDefault:"false"`
EnableSEV bool `env:"ENABLE_SEV" envDefault:"true"`
QemuBinPath string `env:"BIN_PATH" envDefault:"qemu-system-x86_64"`
TmpFileLoc string `env:"TMP_FILE_LOC" envDefault:"tmp"`
UseSudo bool `env:"USE_SUDO" envDefault:"false"`
EnableSEV bool `env:"ENABLE_SEV" envDefault:"false"`
EnableSEVSNP bool `env:"ENABLE_SEV_SNP" envDefault:"true"`
EnableKVM bool `env:"ENABLE_KVM" envDefault:"true"`
// machine, CPU, RAM
Machine string `env:"MACHINE" envDefault:"q35"`
CPU string `env:"CPU" envDefault:"EPYC"`
SmpCount int `env:"SMP_COUNT" envDefault:"4"`
MaxCpus int `env:"SMP_MAXCPUS" envDefault:"64"`
SMPCount int `env:"SMP_COUNT" envDefault:"4"`
MaxCPUs int `env:"SMP_MAXCPUS" envDefault:"64"`
MemID string `env:"MEM_ID" envDefault:"ram1"`
MemoryConfig
// Kernel hash
KernelHash bool `env:"KERNEL_HASH" envDefault:"false"`
// OVMF
OVMFCodeConfig
OVMFVarsConfig
@@ -78,8 +86,10 @@ type Config struct {
NetDevConfig
VirtioNetPciConfig
// Vsock
VSockConfig
// disk
VirtioScsiPciConfig
DiskImgConfig
// SEV
@@ -90,7 +100,7 @@ type Config struct {
Monitor string `env:"MONITOR" envDefault:"pty"`
}
func constructQemuArgs(config Config, computation string) []string {
func constructQemuArgs(config Config) []string {
args := []string{}
// virtualization
@@ -107,7 +117,7 @@ func constructQemuArgs(config Config, computation string) []string {
args = append(args, "-cpu", config.CPU)
}
args = append(args, "-smp", fmt.Sprintf("%d,maxcpus=%d", config.SmpCount, config.MaxCpus))
args = append(args, "-smp", fmt.Sprintf("%d,maxcpus=%d", config.SMPCount, config.MaxCPUs))
args = append(args, "-m", fmt.Sprintf("%s,slots=%d,maxmem=%s",
config.MemoryConfig.Size,
@@ -123,25 +133,14 @@ func constructQemuArgs(config Config, computation string) []string {
config.OVMFCodeConfig.File,
config.OVMFCodeConfig.ReadOnly))
args = append(args, "-drive",
fmt.Sprintf("if=%s,format=%s,unit=%d,file=%s",
config.OVMFVarsConfig.If,
config.OVMFVarsConfig.Format,
config.OVMFVarsConfig.Unit,
config.OVMFVarsConfig.File))
// disk
args = append(args, "-device",
fmt.Sprintf("virtio-scsi-pci,id=%s,disable-legacy=%s,iommu_platform=%t",
config.VirtioScsiPciConfig.ID,
config.VirtioScsiPciConfig.DisableLegacy,
config.VirtioScsiPciConfig.IOMMUPlatform))
args = append(args, "-kernel", config.DiskImgConfig.KernelFile)
args = append(args, "-append", fmt.Sprintf("earlyprintk=serial console=ttyS0 computation=%s", computation))
args = append(args, "-initrd", config.DiskImgConfig.RootFsFile)
if !config.KernelHash {
args = append(args, "-drive",
fmt.Sprintf("if=%s,format=%s,unit=%d,file=%s",
config.OVMFVarsConfig.If,
config.OVMFVarsConfig.Format,
config.OVMFVarsConfig.Unit,
config.OVMFVarsConfig.File))
}
// network
args = append(args, "-netdev",
@@ -150,19 +149,50 @@ func constructQemuArgs(config Config, computation string) []string {
config.NetDevConfig.HostFwdAgent, config.NetDevConfig.GuestFwdAgent))
args = append(args, "-device",
fmt.Sprintf("virtio-net-pci,disable-legacy=%s,iommu_platform=%v,netdev=%s,romfile=%s",
fmt.Sprintf("virtio-net-pci,disable-legacy=%s,iommu_platform=%v,netdev=%s,addr=%s,romfile=%s",
config.VirtioNetPciConfig.DisableLegacy,
config.VirtioNetPciConfig.IOMMUPlatform,
config.NetDevConfig.ID,
config.VirtioNetPciConfig.Addr,
config.VirtioNetPciConfig.ROMFile))
// SEV
if config.EnableSEV {
args = append(args, "-device", fmt.Sprintf("vhost-vsock-pci,id=%s,guest-cid=%d", config.VSockConfig.ID, config.VSockConfig.GuestCID))
args = append(args, "-vnc", fmt.Sprintf(":%d", config.vnc))
if config.EnableSEVSNP {
args = append(args, "-object",
fmt.Sprintf("sev-guest,id=%s,cbitpos=%d,reduced-phys-bits=%d",
fmt.Sprintf("memory-backend-memfd-private,id=%s,size=%s,share=true",
config.MemID,
config.MemoryConfig.Size))
args = append(args, "-machine",
fmt.Sprintf("memory-backend=%s,kvm-type=protected",
config.MemID))
}
args = append(args, "-kernel", config.DiskImgConfig.KernelFile)
args = append(args, "-append", strconv.Quote("earlyprintk=serial console=ttyS0"))
args = append(args, "-initrd", config.DiskImgConfig.RootFsFile)
// SEV
if config.EnableSEV || config.EnableSEVSNP {
sevType := "sev-guest"
kernelHash := ""
if config.EnableSEVSNP {
sevType = "sev-snp-guest"
}
if config.KernelHash {
kernelHash = ",discard=none,kernel-hashes=on"
}
args = append(args, "-object",
fmt.Sprintf("%s,id=%s,cbitpos=%d,reduced-phys-bits=%d%s",
sevType,
config.SevConfig.ID,
config.SevConfig.CBitPos,
config.SevConfig.ReducedPhysBits))
config.SevConfig.ReducedPhysBits,
kernelHash))
args = append(args, "-machine",
fmt.Sprintf("memory-encryption=%s", config.SevConfig.ID))
+18 -26
View File
@@ -4,46 +4,43 @@ package qemu
import (
"context"
"encoding/json"
"fmt"
"os/exec"
"strconv"
"github.com/gofrs/uuid"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/internal"
)
const (
qemuRelPath = "qemu-system-x86_64"
firmwareVars = "OVMF_VARS"
KernelFile = "bzImage"
rootfsFile = "rootfs.cpio"
)
func CreateVM(ctx context.Context, cfg Config, ac agent.Computation) (*exec.Cmd, error) {
// Create unique emu device identifiers.
func CreateVM(ctx context.Context, cfg Config) (*exec.Cmd, error) {
// Create unique emu device identifiers
id, err := uuid.NewV4()
if err != nil {
return &exec.Cmd{}, err
}
qemuCfg := cfg
qemuCfg.NetDevConfig.ID = fmt.Sprintf("%s-%s", qemuCfg.NetDevConfig.ID, id)
qemuCfg.VirtioScsiPciConfig.ID = fmt.Sprintf("%s-%s", qemuCfg.VirtioScsiPciConfig.ID, id)
qemuCfg.SevConfig.ID = fmt.Sprintf("%s-%s", qemuCfg.SevConfig.ID, id)
// Copy firmware vars file.
srcFile := qemuCfg.OVMFVarsConfig.File
dstFile := fmt.Sprintf("%s/%s-%s.fd", cfg.TmpFileLoc, firmwareVars, id)
err = internal.CopyFile(srcFile, dstFile)
if err != nil {
return &exec.Cmd{}, err
if !cfg.KernelHash {
// Copy firmware vars file
srcFile := qemuCfg.OVMFVarsConfig.File
dstFile := fmt.Sprintf("%s/%s-%s.fd", cfg.TmpFileLoc, firmwareVars, id)
err = internal.CopyFile(srcFile, dstFile)
if err != nil {
return &exec.Cmd{}, err
}
qemuCfg.OVMFVarsConfig.File = dstFile
}
qemuCfg.OVMFVarsConfig.File = dstFile
// Copy img files.
srcFile = qemuCfg.DiskImgConfig.KernelFile
dstFile = fmt.Sprintf("%s/%s-%s", cfg.TmpFileLoc, KernelFile, id)
// Copy img files
srcFile := qemuCfg.DiskImgConfig.KernelFile
dstFile := fmt.Sprintf("%s/%s-%s", cfg.TmpFileLoc, KernelFile, id)
err = internal.CopyFile(srcFile, dstFile)
if err != nil {
return &exec.Cmd{}, err
@@ -58,7 +55,7 @@ func CreateVM(ctx context.Context, cfg Config, ac agent.Computation) (*exec.Cmd,
}
qemuCfg.DiskImgConfig.RootFsFile = dstFile
exe, args, err := ExecutableAndArgs(qemuCfg, ac)
exe, args, err := ExecutableAndArgs(qemuCfg)
if err != nil {
return &exec.Cmd{}, err
}
@@ -70,18 +67,13 @@ func CreateVM(ctx context.Context, cfg Config, ac agent.Computation) (*exec.Cmd,
return cmd, nil
}
func ExecutableAndArgs(cfg Config, ac agent.Computation) (string, []string, error) {
exe, err := exec.LookPath(qemuRelPath)
func ExecutableAndArgs(cfg Config) (string, []string, error) {
exe, err := exec.LookPath(cfg.QemuBinPath)
if err != nil {
return "", nil, err
}
cmpBytes, err := json.Marshal(ac)
if err != nil {
return "", nil, err
}
args := constructQemuArgs(cfg, strconv.Quote(string(cmpBytes)))
args := constructQemuArgs(cfg)
if cfg.UseSudo {
args = append([]string{exe}, args...)
+63 -22
View File
@@ -6,16 +6,20 @@ import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net"
"strconv"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/errors"
"github.com/cenkalti/backoff/v4"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/internal/events"
"github.com/ultravioletrs/cocos/manager/qemu"
"github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/types/known/timestamppb"
)
const hashLength = 32
var (
// ErrMalformedEntity indicates malformed entity specification (e.g.
// invalid username or password).
@@ -30,46 +34,64 @@ var (
// ErrFailedToAllocatePort indicates no free port was found on host.
ErrFailedToAllocatePort = errors.New("failed to allocate free port on host")
errInvalidHashLength = errors.New("hash must be of byte length 32")
)
// Service specifies an API that must be fulfilled by the domain service
// implementation, and all of its decorators (e.g. logging & metrics).
type Service interface {
Run(ctx context.Context, c *Computation) (string, error)
// Run create a computation.
Run(ctx context.Context, c *manager.ComputationRunReq) (string, error)
// RetrieveAgentEventsLogs Retrieve and forward agent logs and events via vsock.
RetrieveAgentEventsLogs()
}
type managerService struct {
qemuCfg qemu.Config
logger mglog.Logger
eventSvc events.Service
hostIP string
qemuCfg qemu.Config
logger *slog.Logger
agents map[int]string // agent map of vsock cid to computationID.
eventsChan chan *manager.ClientStreamMessage
}
var _ Service = (*managerService)(nil)
// New instantiates the manager service implementation.
func New(qemuCfg qemu.Config, logger mglog.Logger, eventSvc events.Service, hostIP string) Service {
return &managerService{
qemuCfg: qemuCfg,
eventSvc: eventSvc,
hostIP: hostIP,
logger: logger,
func New(qemuCfg qemu.Config, logger *slog.Logger, eventsChan chan *manager.ClientStreamMessage) Service {
ms := &managerService{
qemuCfg: qemuCfg,
logger: logger,
agents: make(map[int]string),
eventsChan: eventsChan,
}
return ms
}
func (ms *managerService) Run(ctx context.Context, c *Computation) (string, error) {
func (ms *managerService) Run(ctx context.Context, c *manager.ComputationRunReq) (string, error) {
ms.publishEvent("vm-provision", c.Id, "starting", json.RawMessage{})
ac := agent.Computation{
ID: c.Id,
Name: c.Name,
Description: c.Description,
ResultConsumers: c.ResultConsumers,
AgentConfig: agent.AgentConfig{
Port: c.AgentConfig.Port,
Host: c.AgentConfig.Host,
KeyFile: c.AgentConfig.KeyFile,
CertFile: c.AgentConfig.CertFile,
ServerCAFile: c.AgentConfig.ServerCaFile,
ClientCAFile: c.AgentConfig.ClientCaFile,
LogLevel: c.AgentConfig.LogLevel,
},
}
for _, algo := range c.Algorithms {
ac.Algorithms = append(ac.Algorithms, agent.Algorithm{ID: algo.Id, Provider: algo.Provider})
}
ac.Algorithm = agent.Algorithm{ID: c.Algorithm.Id, Provider: c.Algorithm.Provider, Hash: [hashLength]byte(c.Algorithm.Hash)}
for _, data := range c.Datasets {
ac.Datasets = append(ac.Datasets, agent.Dataset{ID: data.Id, Provider: data.Provider})
if len(data.Hash) != hashLength {
ms.publishEvent("vm-provision", c.Id, "failed", json.RawMessage{})
return "", errInvalidHashLength
}
ac.Datasets = append(ac.Datasets, agent.Dataset{ID: data.Id, Provider: data.Provider, Hash: [hashLength]byte(data.Hash)})
}
agentPort, err := getFreePort()
@@ -80,13 +102,23 @@ func (ms *managerService) Run(ctx context.Context, c *Computation) (string, erro
ms.qemuCfg.HostFwdAgent = agentPort
ms.publishEvent("vm-provision", c.Id, "in-progress", json.RawMessage{})
if _, err = qemu.CreateVM(ctx, ms.qemuCfg, ac); err != nil {
if _, err = qemu.CreateVM(ctx, ms.qemuCfg); err != nil {
ms.publishEvent("vm-provision", c.Id, "failed", json.RawMessage{})
return "", err
}
ms.agents[ms.qemuCfg.VSockConfig.GuestCID] = c.Id
err = backoff.Retry(func() error {
return SendAgentConfig(uint32(ms.qemuCfg.VSockConfig.GuestCID), ac)
}, backoff.NewExponentialBackOff())
if err != nil {
return "", err
}
ms.qemuCfg.VSockConfig.GuestCID++
ms.publishEvent("vm-provision", c.Id, "complete", json.RawMessage{})
return fmt.Sprintf("%s:%d", ms.hostIP, ms.qemuCfg.HostFwdAgent), nil
return fmt.Sprint(ms.qemuCfg.HostFwdAgent), nil
}
func getFreePort() (int, error) {
@@ -107,7 +139,16 @@ func getFreePort() (int, error) {
}
func (ms *managerService) publishEvent(event, cmpID, status string, details json.RawMessage) {
if err := ms.eventSvc.SendEvent(event, cmpID, status, details); err != nil {
ms.logger.Warn(err.Error())
ms.eventsChan <- &manager.ClientStreamMessage{
Message: &manager.ClientStreamMessage_AgentEvent{
AgentEvent: &manager.AgentEvent{
EventType: event,
ComputationId: cmpID,
Status: status,
Details: details,
Timestamp: timestamppb.Now(),
Originator: "manager",
},
},
}
}
+6 -1
View File
@@ -6,6 +6,7 @@ import (
"context"
"github.com/ultravioletrs/cocos/manager"
pkgmanager "github.com/ultravioletrs/cocos/pkg/manager"
"go.opentelemetry.io/otel/trace"
)
@@ -21,9 +22,13 @@ func New(svc manager.Service, tracer trace.Tracer) manager.Service {
return &tracingMiddleware{tracer, svc}
}
func (tm *tracingMiddleware) Run(ctx context.Context, mc *manager.Computation) (string, error) {
func (tm *tracingMiddleware) Run(ctx context.Context, mc *pkgmanager.ComputationRunReq) (string, error) {
ctx, span := tm.tracer.Start(ctx, "run")
defer span.End()
return tm.svc.Run(ctx, mc)
}
func (tm *tracingMiddleware) RetrieveAgentEventsLogs() {
tm.svc.RetrieveAgentEventsLogs()
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package manager
import (
"encoding/json"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/agent"
)
const VsockConfigPort uint32 = 9999
func SendAgentConfig(cid uint32, ac agent.Computation) error {
conn, err := vsock.Dial(cid, VsockConfigPort, nil)
if err != nil {
return err
}
defer conn.Close()
payload, err := json.Marshal(ac)
if err != nil {
return err
}
if _, err := conn.Write(payload); err != nil {
return err
}
return nil
}
+203 -24
View File
@@ -3,25 +3,67 @@
package grpc
import (
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/json"
"fmt"
"os"
"time"
"github.com/absmach/magistrala/pkg/errors"
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/proto/check"
"github.com/google/go-sev-guest/validate"
"github.com/google/go-sev-guest/verify"
"github.com/google/go-sev-guest/verify/trust"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
gogrpc "google.golang.org/grpc"
"golang.org/x/crypto/sha3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
type security int
const (
withoutTLS security = iota
withTLS
withmTLS
)
var (
errGrpcConnect = errors.New("failed to connect to grpc server")
errGrpcClose = errors.New("failed to close grpc connection")
errGrpcConnect = errors.New("failed to connect to grpc server")
errGrpcClose = errors.New("failed to close grpc connection")
errManifestOpen = errors.New("failed to open Manifest")
errManifestMissing = errors.New("failed due to missing Manifest")
errManifestDecode = errors.New("failed to decode Manifest json")
errCertificateParse = errors.New("failed to parse x509 certificate")
errAttVerification = errors.New("attestation verification failed")
errAttValidation = errors.New("attestation validation failed")
errCustomExtension = errors.New("failed due to missing custom extension")
)
var (
customSEVSNPExtensionOID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6}
attestationConfiguration = AttestationConfiguration{}
timeout = time.Minute * 2
maxTryDelay = time.Second * 30
)
type Config struct {
ClientTLS bool `env:"CLIENT_TLS" envDefault:"false"`
CACerts string `env:"CA_CERTS" envDefault:""`
URL string `env:"URL" envDefault:"localhost:7001"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"60s"`
ClientCert string `env:"CLIENT_CERT" envDefault:""`
ClientKey string `env:"CLIENT_KEY" envDefault:""`
ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""`
URL string `env:"URL" envDefault:"localhost:7001"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"60s"`
AttestedTLS bool `env:"ATTESTED_TLS" envDefault:"false"`
Manifest string `env:"MANIFEST" envDefault:""`
}
type AttestationConfiguration struct {
SNPPolicy *check.Policy `json:"snp_policy,omitempty"`
RootOFTrust *check.RootOfTrust `json:"root_of_trust,omitempty"`
}
type Client interface {
@@ -32,13 +74,13 @@ type Client interface {
Secure() string
// Connection returns the gRPC connection.
Connection() *gogrpc.ClientConn
Connection() *grpc.ClientConn
}
type client struct {
*gogrpc.ClientConn
*grpc.ClientConn
cfg Config
secure bool
secure security
}
var _ Client = (*client)(nil)
@@ -65,37 +107,174 @@ func (c *client) Close() error {
}
func (c *client) Secure() string {
if c.secure {
switch c.secure {
case withTLS:
return "with TLS"
case withmTLS:
return "with mTLS"
case withoutTLS:
fallthrough
default:
return "without TLS"
}
return "without TLS"
}
func (c *client) Connection() *gogrpc.ClientConn {
func (c *client) Connection() *grpc.ClientConn {
return c.ClientConn
}
// connect creates new gRPC client and connect to gRPC server.
func connect(cfg Config) (*gogrpc.ClientConn, bool, error) {
var opts []gogrpc.DialOption
secure := false
func connect(cfg Config) (*grpc.ClientConn, security, error) {
opts := []grpc.DialOption{
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
}
secure := withoutTLS
tc := insecure.NewCredentials()
if cfg.ClientTLS && cfg.CACerts != "" {
var err error
tc, err = credentials.NewClientTLSFromFile(cfg.CACerts, "")
if cfg.AttestedTLS {
err := readManifest(cfg)
if err != nil {
return nil, secure, err
return nil, secure, fmt.Errorf("failed to read Manifest %w", err)
}
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: verifyAttestationReportTLS,
}
tc = credentials.NewTLS(tlsConfig)
} else {
if cfg.ServerCAFile != "" {
tlsConfig := &tls.Config{}
// Loading root ca certificates file
rootCA, err := os.ReadFile(cfg.ServerCAFile)
if err != nil {
return nil, secure, fmt.Errorf("failed to load root ca file: %w", err)
}
if len(rootCA) > 0 {
capool := x509.NewCertPool()
if !capool.AppendCertsFromPEM(rootCA) {
return nil, secure, fmt.Errorf("failed to append root ca to tls.Config")
}
tlsConfig.RootCAs = capool
secure = withTLS
}
// Loading mTLS certificates file
if cfg.ClientCert != "" || cfg.ClientKey != "" {
certificate, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
if err != nil {
return nil, secure, fmt.Errorf("failed to client certificate and key %w", err)
}
tlsConfig.Certificates = []tls.Certificate{certificate}
secure = withmTLS
}
tc = credentials.NewTLS(tlsConfig)
}
secure = true
}
opts = append(opts, gogrpc.WithTransportCredentials(tc), gogrpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()))
opts = append(opts, grpc.WithTransportCredentials(tc))
conn, err := gogrpc.Dial(cfg.URL, opts...)
conn, err := grpc.Dial(cfg.URL, opts...)
if err != nil {
return nil, secure, errors.Wrap(errGrpcConnect, err)
}
return conn, secure, nil
}
func readManifest(cfg Config) error {
if cfg.Manifest != "" {
manifest, err := os.Open(cfg.Manifest)
if err != nil {
return errors.Wrap(errManifestOpen, err)
}
defer manifest.Close()
decoder := json.NewDecoder(manifest)
err = decoder.Decode(&attestationConfiguration)
if err != nil {
return errors.Wrap(errManifestDecode, err)
}
return nil
}
return errManifestMissing
}
func verifyAttestationReportTLS(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return errors.Wrap(errCertificateParse, err)
}
for _, ext := range cert.Extensions {
if ext.Id.Equal(customSEVSNPExtensionOID) {
// Check if the certificate is self-signed
err := checkIfCertificateSelfSigned(cert)
if err != nil {
return errors.Wrap(errAttVerification, err)
}
publicKeyBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
if err != nil {
return errors.Wrap(errAttVerification, err)
}
expectedReportData := sha3.Sum512(publicKeyBytes)
attestationConfiguration.SNPPolicy.ReportData = expectedReportData[:]
// Attestation verification and validation
sopts, err := verify.RootOfTrustToOptions(attestationConfiguration.RootOFTrust)
if err != nil {
return errors.Wrap(errAttVerification, err)
}
sopts.Product = attestationConfiguration.SNPPolicy.Product
sopts.Getter = &trust.RetryHTTPSGetter{
Timeout: timeout,
MaxRetryDelay: maxTryDelay,
Getter: &trust.SimpleHTTPSGetter{},
}
attestationPB, err := abi.ReportCertsToProto(ext.Value)
if err != nil {
return errors.Wrap(errAttVerification, err)
}
if err = verify.SnpAttestation(attestationPB, sopts); err != nil {
return errors.Wrap(errAttVerification, err)
}
opts, err := validate.PolicyToOptions(attestationConfiguration.SNPPolicy)
if err != nil {
return errors.Wrap(errAttVerification, err)
}
if err = validate.SnpAttestation(attestationPB, opts); err != nil {
return errors.Wrap(errAttValidation, err)
}
return nil
}
}
return errCustomExtension
}
func checkIfCertificateSelfSigned(cert *x509.Certificate) error {
certPool := x509.NewCertPool()
certPool.AddCert(cert)
opts := x509.VerifyOptions{
Roots: certPool,
CurrentTime: time.Now(),
}
if _, err := cert.Verify(opts); err != nil {
return err
}
return nil
}
+2 -3
View File
@@ -3,9 +3,8 @@
package manager
import (
"github.com/ultravioletrs/cocos/manager"
managerapi "github.com/ultravioletrs/cocos/manager/api/grpc"
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
"github.com/ultravioletrs/cocos/pkg/manager"
)
// NewManagerClient creates new manager gRPC client instance.
@@ -15,5 +14,5 @@ func NewManagerClient(cfg grpc.Config) (grpc.Client, manager.ManagerServiceClien
return nil, nil, err
}
return client, managerapi.NewClient(client.Connection(), cfg.Timeout), nil
return client, manager.NewManagerServiceClient(client.Connection()), nil
}
+928
View File
@@ -0,0 +1,928 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc v4.25.3
// source: manager/manager.proto
package manager
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RunResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AgentPort string `protobuf:"bytes,1,opt,name=agent_port,json=agentPort,proto3" json:"agent_port,omitempty"`
ComputationId string `protobuf:"bytes,2,opt,name=computation_id,json=computationId,proto3" json:"computation_id,omitempty"`
}
func (x *RunResponse) Reset() {
*x = RunResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RunResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RunResponse) ProtoMessage() {}
func (x *RunResponse) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RunResponse.ProtoReflect.Descriptor instead.
func (*RunResponse) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{0}
}
func (x *RunResponse) GetAgentPort() string {
if x != nil {
return x.AgentPort
}
return ""
}
func (x *RunResponse) GetComputationId() string {
if x != nil {
return x.ComputationId
}
return ""
}
type AgentEvent struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
EventType string `protobuf:"bytes,1,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"`
Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
ComputationId string `protobuf:"bytes,3,opt,name=computation_id,json=computationId,proto3" json:"computation_id,omitempty"`
Details []byte `protobuf:"bytes,4,opt,name=details,proto3" json:"details,omitempty"`
Originator string `protobuf:"bytes,5,opt,name=originator,proto3" json:"originator,omitempty"`
Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"`
}
func (x *AgentEvent) Reset() {
*x = AgentEvent{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AgentEvent) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AgentEvent) ProtoMessage() {}
func (x *AgentEvent) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AgentEvent.ProtoReflect.Descriptor instead.
func (*AgentEvent) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{1}
}
func (x *AgentEvent) GetEventType() string {
if x != nil {
return x.EventType
}
return ""
}
func (x *AgentEvent) GetTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.Timestamp
}
return nil
}
func (x *AgentEvent) GetComputationId() string {
if x != nil {
return x.ComputationId
}
return ""
}
func (x *AgentEvent) GetDetails() []byte {
if x != nil {
return x.Details
}
return nil
}
func (x *AgentEvent) GetOriginator() string {
if x != nil {
return x.Originator
}
return ""
}
func (x *AgentEvent) GetStatus() string {
if x != nil {
return x.Status
}
return ""
}
type AgentLog struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
ComputationId string `protobuf:"bytes,2,opt,name=computation_id,json=computationId,proto3" json:"computation_id,omitempty"`
Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"`
Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
}
func (x *AgentLog) Reset() {
*x = AgentLog{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AgentLog) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AgentLog) ProtoMessage() {}
func (x *AgentLog) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AgentLog.ProtoReflect.Descriptor instead.
func (*AgentLog) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{2}
}
func (x *AgentLog) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AgentLog) GetComputationId() string {
if x != nil {
return x.ComputationId
}
return ""
}
func (x *AgentLog) GetLevel() string {
if x != nil {
return x.Level
}
return ""
}
func (x *AgentLog) GetTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.Timestamp
}
return nil
}
type ClientStreamMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Message:
//
// *ClientStreamMessage_AgentLog
// *ClientStreamMessage_AgentEvent
// *ClientStreamMessage_RunRes
Message isClientStreamMessage_Message `protobuf_oneof:"message"`
}
func (x *ClientStreamMessage) Reset() {
*x = ClientStreamMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ClientStreamMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ClientStreamMessage) ProtoMessage() {}
func (x *ClientStreamMessage) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ClientStreamMessage.ProtoReflect.Descriptor instead.
func (*ClientStreamMessage) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{3}
}
func (m *ClientStreamMessage) GetMessage() isClientStreamMessage_Message {
if m != nil {
return m.Message
}
return nil
}
func (x *ClientStreamMessage) GetAgentLog() *AgentLog {
if x, ok := x.GetMessage().(*ClientStreamMessage_AgentLog); ok {
return x.AgentLog
}
return nil
}
func (x *ClientStreamMessage) GetAgentEvent() *AgentEvent {
if x, ok := x.GetMessage().(*ClientStreamMessage_AgentEvent); ok {
return x.AgentEvent
}
return nil
}
func (x *ClientStreamMessage) GetRunRes() *RunResponse {
if x, ok := x.GetMessage().(*ClientStreamMessage_RunRes); ok {
return x.RunRes
}
return nil
}
type isClientStreamMessage_Message interface {
isClientStreamMessage_Message()
}
type ClientStreamMessage_AgentLog struct {
AgentLog *AgentLog `protobuf:"bytes,1,opt,name=agent_log,json=agentLog,proto3,oneof"`
}
type ClientStreamMessage_AgentEvent struct {
AgentEvent *AgentEvent `protobuf:"bytes,2,opt,name=agent_event,json=agentEvent,proto3,oneof"`
}
type ClientStreamMessage_RunRes struct {
RunRes *RunResponse `protobuf:"bytes,3,opt,name=run_res,json=runRes,proto3,oneof"`
}
func (*ClientStreamMessage_AgentLog) isClientStreamMessage_Message() {}
func (*ClientStreamMessage_AgentEvent) isClientStreamMessage_Message() {}
func (*ClientStreamMessage_RunRes) isClientStreamMessage_Message() {}
type ComputationRunReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Datasets []*Dataset `protobuf:"bytes,4,rep,name=datasets,proto3" json:"datasets,omitempty"`
Algorithm *Algorithm `protobuf:"bytes,5,opt,name=algorithm,proto3" json:"algorithm,omitempty"`
ResultConsumers []string `protobuf:"bytes,6,rep,name=result_consumers,json=resultConsumers,proto3" json:"result_consumers,omitempty"`
AgentConfig *AgentConfig `protobuf:"bytes,7,opt,name=agent_config,json=agentConfig,proto3" json:"agent_config,omitempty"`
}
func (x *ComputationRunReq) Reset() {
*x = ComputationRunReq{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ComputationRunReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ComputationRunReq) ProtoMessage() {}
func (x *ComputationRunReq) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ComputationRunReq.ProtoReflect.Descriptor instead.
func (*ComputationRunReq) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{4}
}
func (x *ComputationRunReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *ComputationRunReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ComputationRunReq) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *ComputationRunReq) GetDatasets() []*Dataset {
if x != nil {
return x.Datasets
}
return nil
}
func (x *ComputationRunReq) GetAlgorithm() *Algorithm {
if x != nil {
return x.Algorithm
}
return nil
}
func (x *ComputationRunReq) GetResultConsumers() []string {
if x != nil {
return x.ResultConsumers
}
return nil
}
func (x *ComputationRunReq) GetAgentConfig() *AgentConfig {
if x != nil {
return x.AgentConfig
}
return nil
}
type Dataset struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
Hash []byte `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` // should be sha3.Sum256, 32 byte length.
}
func (x *Dataset) Reset() {
*x = Dataset{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Dataset) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Dataset) ProtoMessage() {}
func (x *Dataset) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Dataset.ProtoReflect.Descriptor instead.
func (*Dataset) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{5}
}
func (x *Dataset) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *Dataset) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Dataset) GetHash() []byte {
if x != nil {
return x.Hash
}
return nil
}
type Algorithm struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
Hash []byte `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` // should be sha3.Sum256, 32 byte length.
}
func (x *Algorithm) Reset() {
*x = Algorithm{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Algorithm) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Algorithm) ProtoMessage() {}
func (x *Algorithm) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Algorithm.ProtoReflect.Descriptor instead.
func (*Algorithm) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{6}
}
func (x *Algorithm) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *Algorithm) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Algorithm) GetHash() []byte {
if x != nil {
return x.Hash
}
return nil
}
type AgentConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Port string `protobuf:"bytes,1,opt,name=port,proto3" json:"port,omitempty"`
Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"`
CertFile string `protobuf:"bytes,3,opt,name=cert_file,json=certFile,proto3" json:"cert_file,omitempty"`
KeyFile string `protobuf:"bytes,4,opt,name=key_file,json=keyFile,proto3" json:"key_file,omitempty"`
ClientCaFile string `protobuf:"bytes,5,opt,name=client_ca_file,json=clientCaFile,proto3" json:"client_ca_file,omitempty"`
ServerCaFile string `protobuf:"bytes,6,opt,name=server_ca_file,json=serverCaFile,proto3" json:"server_ca_file,omitempty"`
LogLevel string `protobuf:"bytes,7,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"`
AttestedTls bool `protobuf:"varint,8,opt,name=attested_tls,json=attestedTls,proto3" json:"attested_tls,omitempty"`
}
func (x *AgentConfig) Reset() {
*x = AgentConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_manager_manager_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AgentConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AgentConfig) ProtoMessage() {}
func (x *AgentConfig) ProtoReflect() protoreflect.Message {
mi := &file_manager_manager_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AgentConfig.ProtoReflect.Descriptor instead.
func (*AgentConfig) Descriptor() ([]byte, []int) {
return file_manager_manager_proto_rawDescGZIP(), []int{7}
}
func (x *AgentConfig) GetPort() string {
if x != nil {
return x.Port
}
return ""
}
func (x *AgentConfig) GetHost() string {
if x != nil {
return x.Host
}
return ""
}
func (x *AgentConfig) GetCertFile() string {
if x != nil {
return x.CertFile
}
return ""
}
func (x *AgentConfig) GetKeyFile() string {
if x != nil {
return x.KeyFile
}
return ""
}
func (x *AgentConfig) GetClientCaFile() string {
if x != nil {
return x.ClientCaFile
}
return ""
}
func (x *AgentConfig) GetServerCaFile() string {
if x != nil {
return x.ServerCaFile
}
return ""
}
func (x *AgentConfig) GetLogLevel() string {
if x != nil {
return x.LogLevel
}
return ""
}
func (x *AgentConfig) GetAttestedTls() bool {
if x != nil {
return x.AttestedTls
}
return false
}
var File_manager_manager_proto protoreflect.FileDescriptor
var file_manager_manager_proto_rawDesc = []byte{
0x0a, 0x15, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x53, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12,
0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xde, 0x01, 0x0a, 0x0a, 0x41, 0x67, 0x65, 0x6e, 0x74,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74,
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x54, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x25,
0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12,
0x1e, 0x0a, 0x0a, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12,
0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x41, 0x67, 0x65, 0x6e,
0x74, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x25,
0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x09, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xbb, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a,
0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74,
0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12,
0x36, 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41,
0x67, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x67, 0x65,
0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x5f, 0x72,
0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00,
0x52, 0x06, 0x72, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x22, 0x9d, 0x02, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12,
0x2c, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61,
0x73, 0x65, 0x74, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x12, 0x30, 0x0a,
0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72,
0x69, 0x74, 0x68, 0x6d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12,
0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d,
0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x22, 0x49, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x12, 0x1a,
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61,
0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x4b,
0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xf9, 0x01, 0x0a, 0x0b,
0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70,
0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12,
0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68,
0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65,
0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x46, 0x69, 0x6c,
0x65, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x5f, 0x66,
0x69, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x43, 0x61, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c,
0x65, 0x76, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c,
0x65, 0x76, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64,
0x5f, 0x74, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65,
0x73, 0x74, 0x65, 0x64, 0x54, 0x6c, 0x73, 0x32, 0x5b, 0x0a, 0x0e, 0x4d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x50, 0x72, 0x6f,
0x63, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d,
0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x22, 0x00,
0x28, 0x01, 0x30, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_manager_manager_proto_rawDescOnce sync.Once
file_manager_manager_proto_rawDescData = file_manager_manager_proto_rawDesc
)
func file_manager_manager_proto_rawDescGZIP() []byte {
file_manager_manager_proto_rawDescOnce.Do(func() {
file_manager_manager_proto_rawDescData = protoimpl.X.CompressGZIP(file_manager_manager_proto_rawDescData)
})
return file_manager_manager_proto_rawDescData
}
var file_manager_manager_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_manager_manager_proto_goTypes = []interface{}{
(*RunResponse)(nil), // 0: manager.RunResponse
(*AgentEvent)(nil), // 1: manager.AgentEvent
(*AgentLog)(nil), // 2: manager.AgentLog
(*ClientStreamMessage)(nil), // 3: manager.ClientStreamMessage
(*ComputationRunReq)(nil), // 4: manager.ComputationRunReq
(*Dataset)(nil), // 5: manager.Dataset
(*Algorithm)(nil), // 6: manager.Algorithm
(*AgentConfig)(nil), // 7: manager.AgentConfig
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
}
var file_manager_manager_proto_depIdxs = []int32{
8, // 0: manager.AgentEvent.timestamp:type_name -> google.protobuf.Timestamp
8, // 1: manager.AgentLog.timestamp:type_name -> google.protobuf.Timestamp
2, // 2: manager.ClientStreamMessage.agent_log:type_name -> manager.AgentLog
1, // 3: manager.ClientStreamMessage.agent_event:type_name -> manager.AgentEvent
0, // 4: manager.ClientStreamMessage.run_res:type_name -> manager.RunResponse
5, // 5: manager.ComputationRunReq.datasets:type_name -> manager.Dataset
6, // 6: manager.ComputationRunReq.algorithm:type_name -> manager.Algorithm
7, // 7: manager.ComputationRunReq.agent_config:type_name -> manager.AgentConfig
3, // 8: manager.ManagerService.Process:input_type -> manager.ClientStreamMessage
4, // 9: manager.ManagerService.Process:output_type -> manager.ComputationRunReq
9, // [9:10] is the sub-list for method output_type
8, // [8:9] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_manager_manager_proto_init() }
func file_manager_manager_proto_init() {
if File_manager_manager_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_manager_manager_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RunResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AgentEvent); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AgentLog); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ClientStreamMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ComputationRunReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Dataset); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Algorithm); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_manager_manager_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AgentConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_manager_manager_proto_msgTypes[3].OneofWrappers = []interface{}{
(*ClientStreamMessage_AgentLog)(nil),
(*ClientStreamMessage_AgentEvent)(nil),
(*ClientStreamMessage_RunRes)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_manager_manager_proto_rawDesc,
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_manager_manager_proto_goTypes,
DependencyIndexes: file_manager_manager_proto_depIdxs,
MessageInfos: file_manager_manager_proto_msgTypes,
}.Build()
File_manager_manager_proto = out.File
file_manager_manager_proto_rawDesc = nil
file_manager_manager_proto_goTypes = nil
file_manager_manager_proto_depIdxs = nil
}
@@ -4,7 +4,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1
// - protoc v4.25.3
// source: manager/manager.proto
package manager
@@ -22,14 +22,14 @@ import (
const _ = grpc.SupportPackageIsVersion7
const (
ManagerService_Run_FullMethodName = "/manager.ManagerService/Run"
ManagerService_Process_FullMethodName = "/manager.ManagerService/Process"
)
// ManagerServiceClient is the client API for ManagerService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ManagerServiceClient interface {
Run(ctx context.Context, in *RunRequest, opts ...grpc.CallOption) (*RunResponse, error)
Process(ctx context.Context, opts ...grpc.CallOption) (ManagerService_ProcessClient, error)
}
type managerServiceClient struct {
@@ -40,20 +40,42 @@ func NewManagerServiceClient(cc grpc.ClientConnInterface) ManagerServiceClient {
return &managerServiceClient{cc}
}
func (c *managerServiceClient) Run(ctx context.Context, in *RunRequest, opts ...grpc.CallOption) (*RunResponse, error) {
out := new(RunResponse)
err := c.cc.Invoke(ctx, ManagerService_Run_FullMethodName, in, out, opts...)
func (c *managerServiceClient) Process(ctx context.Context, opts ...grpc.CallOption) (ManagerService_ProcessClient, error) {
stream, err := c.cc.NewStream(ctx, &ManagerService_ServiceDesc.Streams[0], ManagerService_Process_FullMethodName, opts...)
if err != nil {
return nil, err
}
return out, nil
x := &managerServiceProcessClient{stream}
return x, nil
}
type ManagerService_ProcessClient interface {
Send(*ClientStreamMessage) error
Recv() (*ComputationRunReq, error)
grpc.ClientStream
}
type managerServiceProcessClient struct {
grpc.ClientStream
}
func (x *managerServiceProcessClient) Send(m *ClientStreamMessage) error {
return x.ClientStream.SendMsg(m)
}
func (x *managerServiceProcessClient) Recv() (*ComputationRunReq, error) {
m := new(ComputationRunReq)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// ManagerServiceServer is the server API for ManagerService service.
// All implementations must embed UnimplementedManagerServiceServer
// for forward compatibility
type ManagerServiceServer interface {
Run(context.Context, *RunRequest) (*RunResponse, error)
Process(ManagerService_ProcessServer) error
mustEmbedUnimplementedManagerServiceServer()
}
@@ -61,8 +83,8 @@ type ManagerServiceServer interface {
type UnimplementedManagerServiceServer struct {
}
func (UnimplementedManagerServiceServer) Run(context.Context, *RunRequest) (*RunResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Run not implemented")
func (UnimplementedManagerServiceServer) Process(ManagerService_ProcessServer) error {
return status.Errorf(codes.Unimplemented, "method Process not implemented")
}
func (UnimplementedManagerServiceServer) mustEmbedUnimplementedManagerServiceServer() {}
@@ -77,22 +99,30 @@ func RegisterManagerServiceServer(s grpc.ServiceRegistrar, srv ManagerServiceSer
s.RegisterService(&ManagerService_ServiceDesc, srv)
}
func _ManagerService_Run_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RunRequest)
if err := dec(in); err != nil {
func _ManagerService_Process_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ManagerServiceServer).Process(&managerServiceProcessServer{stream})
}
type ManagerService_ProcessServer interface {
Send(*ComputationRunReq) error
Recv() (*ClientStreamMessage, error)
grpc.ServerStream
}
type managerServiceProcessServer struct {
grpc.ServerStream
}
func (x *managerServiceProcessServer) Send(m *ComputationRunReq) error {
return x.ServerStream.SendMsg(m)
}
func (x *managerServiceProcessServer) Recv() (*ClientStreamMessage, error) {
m := new(ClientStreamMessage)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ManagerServiceServer).Run(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ManagerService_Run_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ManagerServiceServer).Run(ctx, req.(*RunRequest))
}
return interceptor(ctx, in, info, handler)
return m, nil
}
// ManagerService_ServiceDesc is the grpc.ServiceDesc for ManagerService service.
@@ -101,12 +131,14 @@ func _ManagerService_Run_Handler(srv interface{}, ctx context.Context, dec func(
var ManagerService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "manager.ManagerService",
HandlerType: (*ManagerServiceServer)(nil),
Methods: []grpc.MethodDesc{
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
MethodName: "Run",
Handler: _ManagerService_Run_Handler,
StreamName: "Process",
Handler: _ManagerService_Process_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "manager/manager.proto",
}
+17 -19
View File
@@ -4,59 +4,55 @@ package sdk
import (
"context"
"log/slog"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/agent"
)
var _ agent.Service = (*agentSDK)(nil)
const size64 = 64
type agentSDK struct {
client agent.AgentServiceClient
logger mglog.Logger
logger *slog.Logger
}
func NewAgentSDK(log mglog.Logger, agentClient agent.AgentServiceClient) *agentSDK {
func NewAgentSDK(log *slog.Logger, agentClient agent.AgentServiceClient) *agentSDK {
return &agentSDK{
client: agentClient,
logger: log,
}
}
func (sdk *agentSDK) Run(ctx context.Context, ac agent.Computation) (string, error) {
return "", nil
}
func (sdk *agentSDK) Algo(ctx context.Context, algorithm agent.Algorithm) (string, error) {
func (sdk *agentSDK) Algo(ctx context.Context, algorithm agent.Algorithm) error {
request := &agent.AlgoRequest{
Algorithm: algorithm.Algorithm,
Provider: algorithm.Provider,
Id: algorithm.ID,
}
response, err := sdk.client.Algo(ctx, request)
if err != nil {
if _, err := sdk.client.Algo(ctx, request); err != nil {
sdk.logger.Error("Failed to call Algo RPC")
return "", err
return err
}
return response.AlgorithmID, nil
return nil
}
func (sdk *agentSDK) Data(ctx context.Context, dataset agent.Dataset) (string, error) {
func (sdk *agentSDK) Data(ctx context.Context, dataset agent.Dataset) error {
request := &agent.DataRequest{
Dataset: dataset.Dataset,
Provider: dataset.Provider,
Id: dataset.ID,
}
response, err := sdk.client.Data(ctx, request)
if err != nil {
if _, err := sdk.client.Data(ctx, request); err != nil {
sdk.logger.Error("Failed to call Data RPC")
return "", err
return err
}
return response.DatasetID, nil
return nil
}
func (sdk *agentSDK) Result(ctx context.Context, consumer string) ([]byte, error) {
@@ -73,8 +69,10 @@ func (sdk *agentSDK) Result(ctx context.Context, consumer string) ([]byte, error
return response.File, nil
}
func (sdk *agentSDK) Attestation(ctx context.Context) ([]byte, error) {
request := &agent.AttestationRequest{}
func (sdk *agentSDK) Attestation(ctx context.Context, reportData [size64]byte) ([]byte, error) {
request := &agent.AttestationRequest{
ReportData: reportData[:],
}
response, err := sdk.client.Attestation(ctx, request)
if err != nil {
+19
View File
@@ -0,0 +1,19 @@
# Computations Server
Manager service is a grpc client. It connects to computations server.
The server then responds with a run computation request. Once manager service receives the computation request it will launch an agent service in a virtual machine and pass the computation manifest. Agent will then pass logs and events to manager which are forwarded to the server. `main.go` is a sample of how such a server would be implemented. This is a very simple example for testing purposes.
## Configuration
The service is configured using the environment variables from the following table. Note that any unset variables will be replaced with their default values.
| Variable | Description | Default |
| ---------------- | ---------------------------------------- | ------- |
| HOST | Computations service gRPC host | |
| PORT | Computations service gRPC port | 7001 |
| SERVER_CERT | Path to server certificate in pem format | |
| SERVER_KEY | Path to server key in pem format | |
## Running
```shell
go run main.go <dataset_path> <algo_path>
```
+129
View File
@@ -0,0 +1,129 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"fmt"
"log"
"log/slog"
"os"
"strconv"
mglog "github.com/absmach/magistrala/logger"
"github.com/ultravioletrs/cocos/internal/env"
"github.com/ultravioletrs/cocos/internal/server"
grpcserver "github.com/ultravioletrs/cocos/internal/server/grpc"
managergrpc "github.com/ultravioletrs/cocos/manager/api/grpc"
"github.com/ultravioletrs/cocos/pkg/manager"
"golang.org/x/crypto/sha3"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var _ managergrpc.Service = (*svc)(nil)
const (
svcName = "manager_test_server"
defaultPort = "7001"
)
var (
algoPath = "./test/manual/algo/lin_reg.py"
dataPath = "./test/manual/data/iris.csv"
attestedTLS = false
)
type svc struct {
logger *slog.Logger
}
func (s *svc) Run(ipAdress string, reqChan chan *manager.ComputationRunReq) {
s.logger.Debug(fmt.Sprintf("received who am on ip address %s", ipAdress))
algo, err := os.ReadFile(algoPath)
if err != nil {
s.logger.Error(fmt.Sprintf("failed to read algorithm file: %s", err))
return
}
data, err := os.ReadFile(dataPath)
if err != nil {
s.logger.Error(fmt.Sprintf("failed to read data file: %s", err))
return
}
algoHash := sha3.Sum256(algo)
dataHash := sha3.Sum256(data)
reqChan <- &manager.ComputationRunReq{
Id: "1",
Name: "sample computation",
Description: "sample descrption",
Datasets: []*manager.Dataset{{Id: "1", Provider: "provider1", Hash: dataHash[:]}},
Algorithm: &manager.Algorithm{Id: "1", Provider: "provider1", Hash: algoHash[:]},
ResultConsumers: []string{"consumer1"},
AgentConfig: &manager.AgentConfig{
Port: "7002",
LogLevel: "debug",
AttestedTls: attestedTLS,
},
}
}
func main() {
if len(os.Args) < 4 {
log.Fatalf("usage: %s <data-path> <algo-path> <attested-tls-bool>", os.Args[0])
}
dataPath = os.Args[1]
algoPath = os.Args[2]
attestedTLSParam, err := strconv.ParseBool(os.Args[3])
if err != nil {
log.Fatalf("usage: %s <data-path> <algo-path> <attested-tls-bool>, <attested-tls-bool> must be a bool value", os.Args[0])
}
attestedTLS = attestedTLSParam
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
incomingChan := make(chan *manager.ClientStreamMessage)
logger, err := mglog.New(os.Stdout, "debug")
if err != nil {
log.Fatalf(err.Error())
}
go func() {
for incoming := range incomingChan {
switch incoming.Message.(type) {
case *manager.ClientStreamMessage_RunRes:
fmt.Println("received runRes")
case *manager.ClientStreamMessage_AgentEvent:
fmt.Println("received agent event")
case *manager.ClientStreamMessage_AgentLog:
fmt.Println("received agent log")
}
fmt.Println(incoming.Message)
}
}()
registerAgentServiceServer := func(srv *grpc.Server) {
reflection.Register(srv)
manager.RegisterManagerServiceServer(srv, managergrpc.NewServer(ctx, incomingChan, &svc{logger: logger}))
}
grpcServerConfig := server.Config{Port: defaultPort}
if err := env.Parse(&grpcServerConfig, env.Options{}); err != nil {
logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err))
return
}
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAgentServiceServer, logger, nil)
g.Go(func() error {
return gs.Start()
})
g.Go(func() error {
return server.StopHandler(ctx, cancel, logger, svcName, gs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("%s service terminated: %s", svcName, err))
}
}
+12 -11
View File
@@ -2,33 +2,34 @@
## CLI
Throughout the tests, we assume that our current working directory is the root of the `agent` repository, both on the host machine and in the VM.
Throughout the tests, we assume that our current working directory is the root of the `cocos` repository, both on the host machine and in the VM.
### Python requirements
Do this both on the host machine and in the VM.
Do this in the VM.
```sh
apt update
apt install python3-pip
pip3 install pandas scikit-learn
```
### Agent-CLI interaction
In the VM, open a console and start `agent`:
```sh
AGENT_LOG_LEVEL=info AGENT_GRPC_URL=10.0.2.15:7002 go run cmd/agent/main.go
```
Agent is started automatically in the VM when launched but requires configuration and manifest to be passed by manager. Alternatively you can pass configuration using this [simplified script](./agent-config/main.go)
Open console on the host, and run
```sh
export AGENT_GRPC_URL=localhost:7002
# Run CLI to provide manifest
go run cmd/cli/main.go run --computation '{"id":"123","name":"Sample Computation","description":"A sample computation","status":"Processing","owner":"John Doe","start_time":"2023-11-03T12:03:21.705171284+03:00","end_time":"2023-11-03T13:03:21.705171532+03:00","datasets":[{"provider":"Provider1","id":"Dataset1"},{"provider":"Provider2","id":"Dataset2"}],"algorithms":[{"provider":"AlgorithmProvider1","id":"Algorithm1"}],"result_consumers":["Consumer1","Consumer2"],"ttl":3600,"metadata":{"key1":"value1","key2":42}, "timeout": "2m"}'
# For attested TLS, also define the path to the computation.json that contains reference values for the fields of the attestation report
export AGENT_GRPC_MANIFEST=./test/manual/computation/computation.json
export AGENT_GRPC_ATTESTED_TLS=true
# Retieve Attestation
go run cmd/cli/main.go attestation get '<report_data>'
# Validate Attestation
go run cmd/cli/main.go attestation validate '<attesation>' --report_data '<report_data>'
# Run the CLI program with algorithm input
go run cmd/cli/main.go algo test/manual/algo/lin_reg.py Algorithm1 AlgorithmProvider1
+140
View File
@@ -0,0 +1,140 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
// Simplified script to pass configs to agent without manager and read logs and events for manager.
// This tool is meant for testing purposes.
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"strconv"
"github.com/mdlayher/vsock"
"github.com/ultravioletrs/cocos/pkg/manager"
"google.golang.org/protobuf/proto"
)
const VsockConfigPort uint32 = 9999
type AgentConfig struct {
LogLevel string `json:"log_level"`
InstanceID string `json:"instance_id"`
Host string `json:"host"`
Port string `json:"port"`
CertFile string `json:"cert_file"`
KeyFile string `json:"server_key"`
AttestedTls bool `json:"attested_tls"`
}
type Computation struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Datasets Datasets `json:"datasets,omitempty"`
Algorithms Algorithms `json:"algorithms,omitempty"`
ResultConsumers []string `json:"result_consumers,omitempty"`
AgentConfig AgentConfig `json:"agent_config,omitempty"`
}
func (d *Datasets) String() string {
dat, err := json.Marshal(d)
if err != nil {
return ""
}
return string(dat)
}
func (a *Algorithms) String() string {
dat, err := json.Marshal(a)
if err != nil {
return ""
}
return string(dat)
}
type Dataset struct {
Dataset []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Datasets []Dataset
type Algorithm struct {
Algorithm []byte `json:"-"`
Provider string `json:"provider,omitempty"`
ID string `json:"id,omitempty"`
}
type Algorithms []Algorithm
func main() {
attestedTLS := false
if len(os.Args) == 2 {
attestedTLSParam, err := strconv.ParseBool(os.Args[1])
if err != nil {
log.Fatalf("usage: %s <attested-tls> - <attested-tls> must be true or false", os.Args[0])
}
attestedTLS = attestedTLSParam
} else if len(os.Args) > 2 {
log.Fatalf("usage: %s <attested-tls>", os.Args[0])
}
l, err := vsock.Listen(9997, nil)
if err != nil {
log.Fatal(err)
}
ac := Computation{
ID: "123",
Datasets: Datasets{Dataset{ID: "1", Provider: "pr1"}},
Algorithms: Algorithms{Algorithm{ID: "1", Provider: "pr1"}},
ResultConsumers: []string{"1"},
AgentConfig: AgentConfig{
LogLevel: "debug",
Port: "7002",
AttestedTls: attestedTLS,
},
}
fmt.Println(SendAgentConfig(3, ac))
for {
conn, err := l.Accept()
if err != nil {
log.Println(err)
continue
}
b := make([]byte, 1024)
n, err := conn.Read(b)
if err != nil {
log.Println(err)
continue
}
conn.Close()
var mes manager.ClientStreamMessage
if err := proto.Unmarshal(b[:n], &mes); err != nil {
log.Println(err)
}
fmt.Println(mes.String())
}
}
func SendAgentConfig(cid uint32, ac Computation) error {
conn, err := vsock.Dial(cid, VsockConfigPort, nil)
if err != nil {
return err
}
defer conn.Close()
payload, err := json.Marshal(ac)
if err != nil {
return err
}
if _, err := conn.Write(payload); err != nil {
return err
}
return nil
}
+15
View File
@@ -0,0 +1,15 @@
{
"snp_policy": {
"minimum_guest_svn": 0,
"policy": 196608,
"minimum_tcb": 1,
"minimum_version": "1.0",
"minimum_launch_tcb": 1,
"measurement": [232, 141, 188, 114, 162, 221, 214, 6, 150, 248, 3, 173, 230, 39, 48, 120, 105, 243, 15, 242, 79, 67, 112, 128, 44, 119, 216, 226, 170, 255, 212, 154, 58, 68, 231, 30, 20, 235, 228, 42, 43, 1, 95, 191, 51, 113, 19, 72],
"minimum_build": 1
},
"root_of_trust": {
"product": "Milan",
"check_crl": true
}
}