mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-08-07 15:26:26 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be423e0231 | |||
| 92ba15d2de | |||
| 49a66d6f35 | |||
| 8eb1fac9ad | |||
| 4b657e5313 | |||
| 38c2abb294 | |||
| 4e8057f481 | |||
| 85a2b7a6c8 | |||
| 45187d7f41 | |||
| f543cb4363 | |||
| cef47baed7 | |||
| 698bd948ed | |||
| 9c8ddfd2b1 | |||
| 79c66a89c3 | |||
| f52702b631 | |||
| 31c7833c3d | |||
| 64bf7a56ac | |||
| bd59a4a617 | |||
| c9af8a166b | |||
| 17c6accbff | |||
| 2d6d276061 | |||
| e8c2ccc071 | |||
| f1af397aa0 | |||
| 77325753f8 | |||
| 3e474338c5 | |||
| 5960b06126 | |||
| 636d3dcaa0 | |||
| 92f4f0535a | |||
| bf84f45306 | |||
| f5b67ca35b | |||
| 3bb0b2a315 | |||
| 434d58f890 | |||
| 32e2bfb881 | |||
| 26bf5dc643 | |||
| bda3968fdf | |||
| 90807d9576 | |||
| 94c169febb | |||
| 3102114ff3 | |||
| 5c60bc2a48 | |||
| 93f2f2ab46 | |||
| dffefc45d8 | |||
| 9a5cc44b2a | |||
| 676c5f154c | |||
| 71104a210c | |||
| ec306c72b9 | |||
| 12a20c74f7 | |||
| d17eba35eb | |||
| cab2799741 | |||
| e217331e5e | |||
| 7e63921896 | |||
| ebe119686f | |||
| 3fe7507529 | |||
| 1b4a510eb2 | |||
| ff0fd71e7d | |||
| bfaa489bdb | |||
| c14f1d7b6c | |||
| ebc8f1bba4 | |||
| 293c65a3aa |
@@ -9,6 +9,7 @@ on:
|
||||
- "pkg/manager/*.pb.go"
|
||||
- "agent/agent.proto"
|
||||
- "agent/*.pb.go"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
@@ -33,8 +34,8 @@ jobs:
|
||||
|
||||
- name: Set up protoc
|
||||
run: |
|
||||
PROTOC_VERSION=29.3
|
||||
PROTOC_GEN_VERSION=v1.36.4
|
||||
PROTOC_VERSION=29.0
|
||||
PROTOC_GEN_VERSION=v1.36.5
|
||||
PROTOC_GRPC_VERSION=v1.5.1
|
||||
|
||||
# Download and install protoc
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
with:
|
||||
repository: "buildroot/buildroot"
|
||||
path: buildroot
|
||||
ref: 2024.11-rc2
|
||||
ref: 2025.05-rc1
|
||||
|
||||
- name: Build hal
|
||||
run: |
|
||||
|
||||
@@ -22,10 +22,11 @@ jobs:
|
||||
go-version: 1.23.x
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@v7
|
||||
with:
|
||||
version: v1.60
|
||||
version: v2.0.2
|
||||
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
make
|
||||
@@ -35,7 +36,7 @@ jobs:
|
||||
mkdir coverage
|
||||
|
||||
- name: Run Agent tests
|
||||
run: go test --tags embed -v --race -covermode=atomic -coverprofile coverage/agent.out ./agent/...
|
||||
run: go test -v --race -covermode=atomic -coverprofile coverage/agent.out ./agent/...
|
||||
|
||||
- name: Run cli tests
|
||||
run: go test -v --race -covermode=atomic -coverprofile coverage/cli.out ./cli/...
|
||||
|
||||
+77
-67
@@ -1,84 +1,94 @@
|
||||
run:
|
||||
timeout: 3m
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 10
|
||||
max-same-issues: 10
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- makezero
|
||||
text: "with non-zero initialized length"
|
||||
|
||||
linters-settings:
|
||||
importas:
|
||||
no-unaliased: true
|
||||
no-extra-aliases: false
|
||||
alias:
|
||||
- pkg: github.com/absmach/magistrala/logger
|
||||
alias: mglog
|
||||
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- dupImport
|
||||
- importShadow
|
||||
- httpNoBody
|
||||
- paramTypeCombine
|
||||
- emptyStringTest
|
||||
- builtinShadow
|
||||
- exposedSyncMutex
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
disabled-tags:
|
||||
- performance
|
||||
- style
|
||||
- experimental
|
||||
- opinionated
|
||||
stylecheck:
|
||||
checks: ["-ST1000", "-ST1003", "-ST1020", "-ST1021", "-ST1022"]
|
||||
goheader:
|
||||
template: |-
|
||||
Copyright (c) Ultraviolet
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- importas
|
||||
- gocritic
|
||||
- gosimple
|
||||
- errcheck
|
||||
- govet
|
||||
- unused
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- ineffassign
|
||||
- misspell
|
||||
- stylecheck
|
||||
- whitespace
|
||||
- gci
|
||||
- gofmt
|
||||
- goimports
|
||||
- loggercheck
|
||||
- goheader
|
||||
- asasalint
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- contextcheck
|
||||
- copyloopvar
|
||||
- decorder
|
||||
- dogsled
|
||||
- dupword
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
- copyloopvar
|
||||
- ginkgolinter
|
||||
- gocheckcompilerdirectives
|
||||
- gofumpt
|
||||
- goconst
|
||||
- gocritic
|
||||
- godot
|
||||
- godox
|
||||
- goheader
|
||||
- goprintffuncname
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- loggercheck
|
||||
- makezero
|
||||
- mirror
|
||||
- misspell
|
||||
- nakedret
|
||||
- dupword
|
||||
|
||||
- staticcheck
|
||||
- unused
|
||||
- whitespace
|
||||
settings:
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- dupImport
|
||||
- importShadow
|
||||
- httpNoBody
|
||||
- paramTypeCombine
|
||||
- emptyStringTest
|
||||
- builtinShadow
|
||||
- exposedSyncMutex
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
disabled-tags:
|
||||
- performance
|
||||
- style
|
||||
- experimental
|
||||
- opinionated
|
||||
goheader:
|
||||
template: |-
|
||||
Copyright (c) Ultraviolet
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
staticcheck:
|
||||
checks:
|
||||
- -ST1000
|
||||
- -ST1003
|
||||
- -ST1020
|
||||
- -ST1021
|
||||
- -ST1022
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- makezero
|
||||
text: with non-zero initialized length
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
max-issues-per-linter: 10
|
||||
max-same-issues: 10
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
BUILD_DIR = build
|
||||
SERVICES = manager agent cli
|
||||
ATTESTATION_POLICY = attestation_policy
|
||||
CGO_ENABLED ?= 1
|
||||
CGO_ENABLED ?= 0
|
||||
GOARCH ?= amd64
|
||||
VERSION ?= $(shell git describe --abbrev=0 --tags --always)
|
||||
COMMIT ?= $(shell git rev-parse HEAD)
|
||||
@@ -17,9 +17,9 @@ IGVM_BUILD_SCRIPT := ./scripts/igvmmeasure/igvm.sh
|
||||
define compile_service
|
||||
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) \
|
||||
go build -ldflags "-s -w \
|
||||
-X 'github.com/absmach/magistrala.BuildTime=$(TIME)' \
|
||||
-X 'github.com/absmach/magistrala.Version=$(VERSION)' \
|
||||
-X 'github.com/absmach/magistrala.Commit=$(COMMIT)'" \
|
||||
-X 'github.com/absmach/supermq.BuildTime=$(TIME)' \
|
||||
-X 'github.com/absmach/supermq.Version=$(VERSION)' \
|
||||
-X 'github.com/absmach/supermq.Commit=$(COMMIT)'" \
|
||||
$(if $(filter 1,$(EMBED_ENABLED)),-tags "embed",) \
|
||||
-o ${BUILD_DIR}/cocos-$(1) cmd/$(1)/main.go
|
||||
endef
|
||||
|
||||
@@ -76,3 +76,5 @@ Cocos AI is published under the permissive open-source [Apache-2.0](LICENSE) lic
|
||||
- [Official Releases](https://github.com/ultravioletrs/cocos/releases)
|
||||
- [Confidential Computing Overview](https://confidentialcomputing.io/white-papers-reports/)
|
||||
- [Trusted Execution Environments (TEEs)](https://en.wikipedia.org/wiki/Trusted_execution_environment)
|
||||
|
||||
>This work has been partially supported by the [ELASTIC project](https://elasticproject.eu/), which received funding from the Smart Networks and Services Joint Undertaking (SNS JU) under the European Union’s Horizon Europe research and innovation programme under [Grant Agreement No. 101139067](https://cordis.europa.eu/project/id/101139067). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union. Neither the European Union nor the granting authority can be held responsible for them.
|
||||
|
||||
+10
-10
@@ -6,16 +6,16 @@ Agent service provides a barebones HTTP and gRPC API and Service interface imple
|
||||
|
||||
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 |
|
||||
| ------------------------------ | ------------------------------------------------------ | ------------------------------ |
|
||||
| AGENT_LOG_LEVEL | Log level for agent service (debug, info, warn, error) | debug |
|
||||
| AGENT_CVM_GRPC_HOST | Agent service gRPC host | "" |
|
||||
| AGENT_CVM_GRPC_PORT | Agent service gRPC port | 7001 |
|
||||
| AGENT_CVM_GRPC_SERVER_CERT | Path to gRPC server certificate in pem format | "" |
|
||||
| AGENT_CVM_GRPC_SERVER_KEY | Path to gRPC server key in pem format | "" |
|
||||
| AGENT_CVM_GRPC_SERVER_CA_CERTS | Path to gRPC server CA certificate | "" |
|
||||
| AGENT_CVM_GRPC_CLIENT_CA_CERTS | Path to gRPC client CA certificate | "" |
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------ |
|
||||
| AGENT_LOG_LEVEL | Log level for agent service (debug, info, warn, error) | debug |
|
||||
| AGENT_CVM_GRPC_HOST | Agent service gRPC host | "" |
|
||||
| AGENT_CVM_GRPC_PORT | Agent service gRPC port | 7001 |
|
||||
| AGENT_CVM_GRPC_SERVER_CERT | Path to gRPC server certificate in pem format | "" |
|
||||
| AGENT_CVM_GRPC_SERVER_KEY | Path to gRPC server key in pem format | "" |
|
||||
| AGENT_CVM_GRPC_SERVER_CA_CERTS | Path to gRPC server CA certificate | "" |
|
||||
| AGENT_CVM_GRPC_CLIENT_CA_CERTS | Path to gRPC client CA certificate | "" |
|
||||
| AGENT_CVM_CA_URL | URL for CA service, if provided it will be used for certificate generation, used only with aTLS at the moment | "" |
|
||||
|
||||
## Deployment
|
||||
|
||||
|
||||
+244
-27
@@ -3,8 +3,8 @@
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.4
|
||||
// protoc v5.29.3
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc v5.29.0
|
||||
// source: agent/agent.proto
|
||||
|
||||
package agent
|
||||
@@ -384,6 +384,190 @@ func (x *AttestationResponse) GetFile() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type IMAMeasurementsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsRequest) Reset() {
|
||||
*x = IMAMeasurementsRequest{}
|
||||
mi := &file_agent_agent_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*IMAMeasurementsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *IMAMeasurementsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_agent_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use IMAMeasurementsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*IMAMeasurementsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_agent_agent_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
type IMAMeasurementsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
|
||||
Pcr10 []byte `protobuf:"bytes,2,opt,name=pcr10,proto3" json:"pcr10,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsResponse) Reset() {
|
||||
*x = IMAMeasurementsResponse{}
|
||||
mi := &file_agent_agent_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*IMAMeasurementsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *IMAMeasurementsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_agent_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use IMAMeasurementsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*IMAMeasurementsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_agent_agent_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsResponse) GetFile() []byte {
|
||||
if x != nil {
|
||||
return x.File
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *IMAMeasurementsResponse) GetPcr10() []byte {
|
||||
if x != nil {
|
||||
return x.Pcr10
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AttestationResultRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
TokenNonce []byte `protobuf:"bytes,1,opt,name=tokenNonce,proto3" json:"tokenNonce,omitempty"` // Should be less or equal 32 bytes
|
||||
Type int32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AttestationResultRequest) Reset() {
|
||||
*x = AttestationResultRequest{}
|
||||
mi := &file_agent_agent_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AttestationResultRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AttestationResultRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AttestationResultRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_agent_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AttestationResultRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AttestationResultRequest) Descriptor() ([]byte, []int) {
|
||||
return file_agent_agent_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *AttestationResultRequest) GetTokenNonce() []byte {
|
||||
if x != nil {
|
||||
return x.TokenNonce
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AttestationResultRequest) GetType() int32 {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AttestationResultResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AttestationResultResponse) Reset() {
|
||||
*x = AttestationResultResponse{}
|
||||
mi := &file_agent_agent_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AttestationResultResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AttestationResultResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AttestationResultResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_agent_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AttestationResultResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AttestationResultResponse) Descriptor() ([]byte, []int) {
|
||||
return file_agent_agent_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *AttestationResultResponse) GetFile() []byte {
|
||||
if x != nil {
|
||||
return x.File
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_agent_agent_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_agent_agent_proto_rawDesc = string([]byte{
|
||||
@@ -412,7 +596,21 @@ var file_agent_agent_proto_rawDesc = string([]byte{
|
||||
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 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, 0xfd, 0x01, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74,
|
||||
0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x49, 0x4d, 0x41, 0x4d, 0x65, 0x61,
|
||||
0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x22, 0x43, 0x0a, 0x17, 0x49, 0x4d, 0x41, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x73, 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, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x70, 0x63, 0x72, 0x31, 0x30, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
|
||||
0x70, 0x63, 0x72, 0x31, 0x30, 0x22, 0x4e, 0x0a, 0x18, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4e, 0x6f, 0x6e, 0x63,
|
||||
0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x19, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 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, 0x32, 0xad, 0x03, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 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,
|
||||
@@ -428,7 +626,18 @@ var file_agent_agent_proto_rawDesc = string([]byte{
|
||||
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, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x61, 0x67, 0x65, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x54, 0x0a, 0x0f, 0x49, 0x4d, 0x41, 0x4d, 0x65, 0x61,
|
||||
0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x67, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x49, 0x4d, 0x41, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x49, 0x4d, 0x41, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x58, 0x0a, 0x11,
|
||||
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
|
||||
0x74, 0x12, 0x1f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 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,
|
||||
})
|
||||
|
||||
@@ -444,31 +653,39 @@ func file_agent_agent_proto_rawDescGZIP() []byte {
|
||||
return file_agent_agent_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_agent_agent_proto_goTypes = []any{
|
||||
(*AlgoRequest)(nil), // 0: agent.AlgoRequest
|
||||
(*AlgoResponse)(nil), // 1: agent.AlgoResponse
|
||||
(*DataRequest)(nil), // 2: agent.DataRequest
|
||||
(*DataResponse)(nil), // 3: agent.DataResponse
|
||||
(*ResultRequest)(nil), // 4: agent.ResultRequest
|
||||
(*ResultResponse)(nil), // 5: agent.ResultResponse
|
||||
(*AttestationRequest)(nil), // 6: agent.AttestationRequest
|
||||
(*AttestationResponse)(nil), // 7: agent.AttestationResponse
|
||||
(*AlgoRequest)(nil), // 0: agent.AlgoRequest
|
||||
(*AlgoResponse)(nil), // 1: agent.AlgoResponse
|
||||
(*DataRequest)(nil), // 2: agent.DataRequest
|
||||
(*DataResponse)(nil), // 3: agent.DataResponse
|
||||
(*ResultRequest)(nil), // 4: agent.ResultRequest
|
||||
(*ResultResponse)(nil), // 5: agent.ResultResponse
|
||||
(*AttestationRequest)(nil), // 6: agent.AttestationRequest
|
||||
(*AttestationResponse)(nil), // 7: agent.AttestationResponse
|
||||
(*IMAMeasurementsRequest)(nil), // 8: agent.IMAMeasurementsRequest
|
||||
(*IMAMeasurementsResponse)(nil), // 9: agent.IMAMeasurementsResponse
|
||||
(*AttestationResultRequest)(nil), // 10: agent.AttestationResultRequest
|
||||
(*AttestationResultResponse)(nil), // 11: agent.AttestationResultResponse
|
||||
}
|
||||
var file_agent_agent_proto_depIdxs = []int32{
|
||||
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
|
||||
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
|
||||
8, // 4: agent.AgentService.IMAMeasurements:input_type -> agent.IMAMeasurementsRequest
|
||||
10, // 5: agent.AgentService.AttestationResult:input_type -> agent.AttestationResultRequest
|
||||
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
|
||||
9, // 10: agent.AgentService.IMAMeasurements:output_type -> agent.IMAMeasurementsResponse
|
||||
11, // 11: agent.AgentService.AttestationResult:output_type -> agent.AttestationResultResponse
|
||||
6, // [6:12] is the sub-list for method output_type
|
||||
0, // [0:6] 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() }
|
||||
@@ -482,7 +699,7 @@ func file_agent_agent_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_agent_proto_rawDesc), len(file_agent_agent_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 8,
|
||||
NumMessages: 12,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -12,6 +12,8 @@ service AgentService {
|
||||
rpc Data(stream DataRequest) returns (DataResponse) {}
|
||||
rpc Result(ResultRequest) returns (stream ResultResponse) {}
|
||||
rpc Attestation(AttestationRequest) returns (stream AttestationResponse) {}
|
||||
rpc IMAMeasurements(IMAMeasurementsRequest) returns (stream IMAMeasurementsResponse) {}
|
||||
rpc AttestationResult(AttestationResultRequest) returns (AttestationResultResponse) {}
|
||||
}
|
||||
|
||||
message AlgoRequest {
|
||||
@@ -44,3 +46,19 @@ message AttestationRequest {
|
||||
message AttestationResponse {
|
||||
bytes file = 1;
|
||||
}
|
||||
|
||||
message IMAMeasurementsRequest {
|
||||
}
|
||||
|
||||
message IMAMeasurementsResponse {
|
||||
bytes file = 1;
|
||||
bytes pcr10 = 2;
|
||||
}
|
||||
|
||||
message AttestationResultRequest{
|
||||
bytes tokenNonce = 1; // Should be less or equal 32 bytes
|
||||
int32 type = 3;
|
||||
}
|
||||
message AttestationResultResponse{
|
||||
bytes file = 1;
|
||||
}
|
||||
|
||||
+86
-6
@@ -4,7 +4,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.29.3
|
||||
// - protoc v5.29.0
|
||||
// source: agent/agent.proto
|
||||
|
||||
package agent
|
||||
@@ -22,10 +22,12 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
AgentService_Algo_FullMethodName = "/agent.AgentService/Algo"
|
||||
AgentService_Data_FullMethodName = "/agent.AgentService/Data"
|
||||
AgentService_Result_FullMethodName = "/agent.AgentService/Result"
|
||||
AgentService_Attestation_FullMethodName = "/agent.AgentService/Attestation"
|
||||
AgentService_Algo_FullMethodName = "/agent.AgentService/Algo"
|
||||
AgentService_Data_FullMethodName = "/agent.AgentService/Data"
|
||||
AgentService_Result_FullMethodName = "/agent.AgentService/Result"
|
||||
AgentService_Attestation_FullMethodName = "/agent.AgentService/Attestation"
|
||||
AgentService_IMAMeasurements_FullMethodName = "/agent.AgentService/IMAMeasurements"
|
||||
AgentService_AttestationResult_FullMethodName = "/agent.AgentService/AttestationResult"
|
||||
)
|
||||
|
||||
// AgentServiceClient is the client API for AgentService service.
|
||||
@@ -36,6 +38,8 @@ type AgentServiceClient interface {
|
||||
Data(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[DataRequest, DataResponse], error)
|
||||
Result(ctx context.Context, in *ResultRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ResultResponse], error)
|
||||
Attestation(ctx context.Context, in *AttestationRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[AttestationResponse], error)
|
||||
IMAMeasurements(ctx context.Context, in *IMAMeasurementsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IMAMeasurementsResponse], error)
|
||||
AttestationResult(ctx context.Context, in *AttestationResultRequest, opts ...grpc.CallOption) (*AttestationResultResponse, error)
|
||||
}
|
||||
|
||||
type agentServiceClient struct {
|
||||
@@ -110,6 +114,35 @@ func (c *agentServiceClient) Attestation(ctx context.Context, in *AttestationReq
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type AgentService_AttestationClient = grpc.ServerStreamingClient[AttestationResponse]
|
||||
|
||||
func (c *agentServiceClient) IMAMeasurements(ctx context.Context, in *IMAMeasurementsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IMAMeasurementsResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &AgentService_ServiceDesc.Streams[4], AgentService_IMAMeasurements_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[IMAMeasurementsRequest, IMAMeasurementsResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type AgentService_IMAMeasurementsClient = grpc.ServerStreamingClient[IMAMeasurementsResponse]
|
||||
|
||||
func (c *agentServiceClient) AttestationResult(ctx context.Context, in *AttestationResultRequest, opts ...grpc.CallOption) (*AttestationResultResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AttestationResultResponse)
|
||||
err := c.cc.Invoke(ctx, AgentService_AttestationResult_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AgentServiceServer is the server API for AgentService service.
|
||||
// All implementations must embed UnimplementedAgentServiceServer
|
||||
// for forward compatibility.
|
||||
@@ -118,6 +151,8 @@ type AgentServiceServer interface {
|
||||
Data(grpc.ClientStreamingServer[DataRequest, DataResponse]) error
|
||||
Result(*ResultRequest, grpc.ServerStreamingServer[ResultResponse]) error
|
||||
Attestation(*AttestationRequest, grpc.ServerStreamingServer[AttestationResponse]) error
|
||||
IMAMeasurements(*IMAMeasurementsRequest, grpc.ServerStreamingServer[IMAMeasurementsResponse]) error
|
||||
AttestationResult(context.Context, *AttestationResultRequest) (*AttestationResultResponse, error)
|
||||
mustEmbedUnimplementedAgentServiceServer()
|
||||
}
|
||||
|
||||
@@ -140,6 +175,12 @@ func (UnimplementedAgentServiceServer) Result(*ResultRequest, grpc.ServerStreami
|
||||
func (UnimplementedAgentServiceServer) Attestation(*AttestationRequest, grpc.ServerStreamingServer[AttestationResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Attestation not implemented")
|
||||
}
|
||||
func (UnimplementedAgentServiceServer) IMAMeasurements(*IMAMeasurementsRequest, grpc.ServerStreamingServer[IMAMeasurementsResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method IMAMeasurements not implemented")
|
||||
}
|
||||
func (UnimplementedAgentServiceServer) AttestationResult(context.Context, *AttestationResultRequest) (*AttestationResultResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AttestationResult not implemented")
|
||||
}
|
||||
func (UnimplementedAgentServiceServer) mustEmbedUnimplementedAgentServiceServer() {}
|
||||
func (UnimplementedAgentServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -197,13 +238,47 @@ func _AgentService_Attestation_Handler(srv interface{}, stream grpc.ServerStream
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type AgentService_AttestationServer = grpc.ServerStreamingServer[AttestationResponse]
|
||||
|
||||
func _AgentService_IMAMeasurements_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(IMAMeasurementsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(AgentServiceServer).IMAMeasurements(m, &grpc.GenericServerStream[IMAMeasurementsRequest, IMAMeasurementsResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type AgentService_IMAMeasurementsServer = grpc.ServerStreamingServer[IMAMeasurementsResponse]
|
||||
|
||||
func _AgentService_AttestationResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AttestationResultRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AgentServiceServer).AttestationResult(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgentService_AttestationResult_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServiceServer).AttestationResult(ctx, req.(*AttestationResultRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AgentService_ServiceDesc is the grpc.ServiceDesc for AgentService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AgentService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "agent.AgentService",
|
||||
HandlerType: (*AgentServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "AttestationResult",
|
||||
Handler: _AgentService_AttestationResult_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Algo",
|
||||
@@ -225,6 +300,11 @@ var AgentService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _AgentService_Attestation_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "IMAMeasurements",
|
||||
Handler: _AgentService_IMAMeasurements_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "agent/agent.proto",
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ func (b *binary) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if b.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.cmd.Process.Kill(); err != nil {
|
||||
return fmt.Errorf("error stopping algorithm: %v", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
mglog "github.com/absmach/magistrala/logger"
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent/events/mocks"
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// Algorithm is an autogenerated mock type for the Algorithm type
|
||||
type Algorithm struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type Algorithm_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *Algorithm) EXPECT() *Algorithm_Expecter {
|
||||
return &Algorithm_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Run provides a mock function with no fields
|
||||
func (_m *Algorithm) Run() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Run")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Algorithm_Run_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Run'
|
||||
type Algorithm_Run_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Run is a helper method to define mock.On call
|
||||
func (_e *Algorithm_Expecter) Run() *Algorithm_Run_Call {
|
||||
return &Algorithm_Run_Call{Call: _e.mock.On("Run")}
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Run_Call) Run(run func()) *Algorithm_Run_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Run_Call) Return(_a0 error) *Algorithm_Run_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Run_Call) RunAndReturn(run func() error) *Algorithm_Run_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Stop provides a mock function with no fields
|
||||
func (_m *Algorithm) Stop() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Stop")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Algorithm_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop'
|
||||
type Algorithm_Stop_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Stop is a helper method to define mock.On call
|
||||
func (_e *Algorithm_Expecter) Stop() *Algorithm_Stop_Call {
|
||||
return &Algorithm_Stop_Call{Call: _e.mock.On("Stop")}
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Stop_Call) Run(run func()) *Algorithm_Stop_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Stop_Call) Return(_a0 error) *Algorithm_Stop_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Algorithm_Stop_Call) RunAndReturn(run func() error) *Algorithm_Stop_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewAlgorithm creates a new instance of Algorithm. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAlgorithm(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Algorithm {
|
||||
mock := &Algorithm{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -114,6 +114,10 @@ func (p *python) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := p.cmd.Process.Kill(); err != nil {
|
||||
return fmt.Errorf("error stopping algorithm: %v", err)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ func (w *wasm) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if w.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := w.cmd.Process.Kill(); err != nil {
|
||||
return fmt.Errorf("error stopping algorithm: %v", err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
)
|
||||
|
||||
func algoEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
@@ -71,7 +71,7 @@ func attestationEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
if err := req.validate(); err != nil {
|
||||
return attestationRes{}, err
|
||||
}
|
||||
file, err := svc.Attestation(ctx, req.TeeNonce, req.VtpmNonce, config.AttestationType(req.AttType))
|
||||
file, err := svc.Attestation(ctx, req.TeeNonce, req.VtpmNonce, attestation.PlatformType(req.AttType))
|
||||
if err != nil {
|
||||
return attestationRes{}, err
|
||||
}
|
||||
@@ -79,3 +79,33 @@ func attestationEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
return attestationRes{File: file}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func imaMeasurementsEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(imaMeasurementsReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return imaMeasurementsRes{}, err
|
||||
}
|
||||
file, pcr10, err := svc.IMAMeasurements(ctx)
|
||||
if err != nil {
|
||||
return imaMeasurementsRes{}, err
|
||||
}
|
||||
|
||||
return imaMeasurementsRes{File: file, PCR10: pcr10}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func attestationResultEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(FetchAttestationResultReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return fetchAttestationResultRes{}, err
|
||||
}
|
||||
file, err := svc.AttestationResult(ctx, req.tokenNonce, attestation.PlatformType(req.AttType))
|
||||
if err != nil {
|
||||
return fetchAttestationResultRes{}, err
|
||||
}
|
||||
return fetchAttestationResultRes{File: file}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"github.com/ultravioletrs/cocos/agent/mocks"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
@@ -142,11 +143,11 @@ func TestAttestationEndpoint(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Success",
|
||||
req: attestationReq{TeeNonce: sha3.Sum512([]byte("report data")), VtpmNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: config.SNP},
|
||||
req: attestationReq{TeeNonce: sha3.Sum512([]byte("report data")), VtpmNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: attestation.SNP},
|
||||
},
|
||||
{
|
||||
name: "Service Error",
|
||||
req: attestationReq{TeeNonce: sha3.Sum512([]byte("report data")), VtpmNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: config.SNP},
|
||||
req: attestationReq{TeeNonce: sha3.Sum512([]byte("report data")), VtpmNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: attestation.SNP},
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
@@ -172,3 +173,55 @@ func TestAttestationEndpoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationResultEndpoint(t *testing.T) {
|
||||
svc := new(mocks.Service)
|
||||
tests := []struct {
|
||||
name string
|
||||
req FetchAttestationResultReq
|
||||
mockErr error
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "Success",
|
||||
req: FetchAttestationResultReq{tokenNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: attestation.AzureToken},
|
||||
mockErr: nil,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "Service Error",
|
||||
req: FetchAttestationResultReq{tokenNonce: sha3.Sum256([]byte("vtpm nonce")), AttType: attestation.AzureToken},
|
||||
mockErr: errors.New("mock failure"),
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Only call service mock if validation is expected to pass
|
||||
if err := tt.req.validate(); err == nil {
|
||||
svc.On("AttestationResult", mock.Anything, tt.req.tokenNonce, attestation.PlatformType(tt.req.AttType)).
|
||||
Return([]byte("mock file"), tt.mockErr).Once()
|
||||
}
|
||||
|
||||
endpoint := attestationResultEndpoint(svc)
|
||||
res, err := endpoint(context.Background(), tt.req)
|
||||
|
||||
if (err != nil) != tt.expectedErr {
|
||||
t.Errorf("attestationResultEndpoint() error = %v, expectedErr %v", err, tt.expectedErr)
|
||||
}
|
||||
|
||||
if !tt.expectedErr {
|
||||
r, ok := res.(fetchAttestationResultRes)
|
||||
if !ok {
|
||||
t.Errorf("attestationResultEndpoint() returned unexpected type %T", res)
|
||||
}
|
||||
if string(r.File) != "mock file" {
|
||||
t.Errorf("expected file content 'mock file', got %s", r.File)
|
||||
}
|
||||
}
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package grpc
|
||||
import (
|
||||
"errors"
|
||||
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
@@ -44,14 +44,34 @@ func (req resultReq) validate() error {
|
||||
type attestationReq struct {
|
||||
TeeNonce [quoteprovider.Nonce]byte
|
||||
VtpmNonce [vtpm.Nonce]byte
|
||||
AttType config.AttestationType
|
||||
AttType attestation.PlatformType
|
||||
}
|
||||
|
||||
type FetchAttestationResultReq struct {
|
||||
tokenNonce [vtpm.Nonce]byte
|
||||
AttType attestation.PlatformType
|
||||
}
|
||||
|
||||
func (req attestationReq) validate() error {
|
||||
switch req.AttType {
|
||||
case config.SNP, config.VTPM, config.SNPvTPM:
|
||||
return validateAttestationType(req.AttType)
|
||||
}
|
||||
|
||||
func (req FetchAttestationResultReq) validate() error {
|
||||
return validateAttestationType(req.AttType)
|
||||
}
|
||||
|
||||
func validateAttestationType(attType attestation.PlatformType) error {
|
||||
switch attType {
|
||||
case attestation.SNP, attestation.VTPM, attestation.SNPvTPM, attestation.TDX, attestation.AzureToken:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("invalid attestation type in attestation request")
|
||||
return errors.New("invalid attestation type")
|
||||
}
|
||||
}
|
||||
|
||||
type imaMeasurementsReq struct{}
|
||||
|
||||
func (req imaMeasurementsReq) validate() error {
|
||||
// No request parameters to validate, so no validation logic needed
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,3 +13,12 @@ type resultRes struct {
|
||||
type attestationRes struct {
|
||||
File []byte
|
||||
}
|
||||
|
||||
type imaMeasurementsRes struct {
|
||||
File []byte
|
||||
PCR10 []byte
|
||||
}
|
||||
|
||||
type fetchAttestationResultRes struct {
|
||||
File []byte `protobuf:"bytes,1,opt,name=AttestationResult,proto3" json:"AttestationResult,omitempty"`
|
||||
}
|
||||
|
||||
+306
-101
@@ -8,10 +8,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/transport/grpc"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -25,49 +27,77 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTEENonceLength = errors.New("malformed report data, expect less or equal to 64 bytes")
|
||||
ErrVTpmNonceLength = errors.New("malformed vTPM nonce, expect less or equal to 32 bytes")
|
||||
ErrTEENonceLength = errors.New("malformed report data, expect less or equal to 64 bytes")
|
||||
ErrVTPMNonceLength = errors.New("malformed vTPM nonce, expect less or equal to 32 bytes")
|
||||
ErrTokenNonceLength = errors.New("malformed token nonce, expect less or equal to 32 bytes")
|
||||
)
|
||||
|
||||
var _ agent.AgentServiceServer = (*grpcServer)(nil)
|
||||
|
||||
type grpcServer struct {
|
||||
algo grpc.Handler
|
||||
data grpc.Handler
|
||||
result grpc.Handler
|
||||
attestation grpc.Handler
|
||||
handlers map[string]grpc.Handler
|
||||
agent.UnimplementedAgentServiceServer
|
||||
}
|
||||
|
||||
type endpointConfig struct {
|
||||
endpoint func(agent.Service) endpoint.Endpoint
|
||||
decodeRequest grpc.DecodeRequestFunc
|
||||
encodeResponse grpc.EncodeResponseFunc
|
||||
}
|
||||
|
||||
// NewServer returns new AgentServiceServer instance.
|
||||
func NewServer(svc agent.Service) agent.AgentServiceServer {
|
||||
// Define endpoint configurations
|
||||
endpoints := map[string]endpointConfig{
|
||||
"algo": {
|
||||
endpoint: algoEndpoint,
|
||||
decodeRequest: decodeAlgoRequest,
|
||||
encodeResponse: encodeAlgoResponse,
|
||||
},
|
||||
"data": {
|
||||
endpoint: dataEndpoint,
|
||||
decodeRequest: decodeDataRequest,
|
||||
encodeResponse: encodeDataResponse,
|
||||
},
|
||||
"result": {
|
||||
endpoint: resultEndpoint,
|
||||
decodeRequest: decodeResultRequest,
|
||||
encodeResponse: encodeResultResponse,
|
||||
},
|
||||
"attestation": {
|
||||
endpoint: attestationEndpoint,
|
||||
decodeRequest: decodeAttestationRequest,
|
||||
encodeResponse: encodeAttestationResponse,
|
||||
},
|
||||
"imaMeasurements": {
|
||||
endpoint: imaMeasurementsEndpoint,
|
||||
decodeRequest: decodeIMAMeasurementsRequest,
|
||||
encodeResponse: encodeIMAMeasurementsResponse,
|
||||
},
|
||||
"attestationResult": {
|
||||
endpoint: attestationResultEndpoint,
|
||||
decodeRequest: decodeAttestationResultRequest,
|
||||
encodeResponse: encodeAttestationResultResponse,
|
||||
},
|
||||
}
|
||||
|
||||
// Create handlers using the configurations
|
||||
handlers := make(map[string]grpc.Handler)
|
||||
for name, config := range endpoints {
|
||||
handlers[name] = grpc.NewServer(
|
||||
config.endpoint(svc),
|
||||
config.decodeRequest,
|
||||
config.encodeResponse,
|
||||
)
|
||||
}
|
||||
|
||||
return &grpcServer{
|
||||
algo: grpc.NewServer(
|
||||
algoEndpoint(svc),
|
||||
decodeAlgoRequest,
|
||||
encodeAlgoResponse,
|
||||
),
|
||||
data: grpc.NewServer(
|
||||
dataEndpoint(svc),
|
||||
decodeDataRequest,
|
||||
encodeDataResponse,
|
||||
),
|
||||
result: grpc.NewServer(
|
||||
resultEndpoint(svc),
|
||||
decodeResultRequest,
|
||||
encodeResultResponse,
|
||||
),
|
||||
attestation: grpc.NewServer(
|
||||
attestationEndpoint(svc),
|
||||
decodeAttestationRequest,
|
||||
encodeAttestationResponse,
|
||||
),
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
func decodeAlgoRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*agent.AlgoRequest)
|
||||
|
||||
return algoReq{
|
||||
Algorithm: req.Algorithm,
|
||||
Requirements: req.Requirements,
|
||||
@@ -80,7 +110,6 @@ func encodeAlgoResponse(_ context.Context, response interface{}) (interface{}, e
|
||||
|
||||
func decodeDataRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*agent.DataRequest)
|
||||
|
||||
return dataReq{
|
||||
Dataset: req.Dataset,
|
||||
Filename: req.Filename,
|
||||
@@ -102,22 +131,47 @@ func encodeResultResponse(_ context.Context, response interface{}) (interface{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateNonce(nonce []byte, maxLen int, target interface{}) error {
|
||||
if len(nonce) > maxLen {
|
||||
switch maxLen {
|
||||
case quoteprovider.Nonce:
|
||||
return ErrTEENonceLength
|
||||
case vtpm.Nonce:
|
||||
return ErrVTPMNonceLength
|
||||
default:
|
||||
return ErrTokenNonceLength
|
||||
}
|
||||
}
|
||||
|
||||
switch t := target.(type) {
|
||||
case *[quoteprovider.Nonce]byte:
|
||||
copy(t[:], nonce)
|
||||
case *[vtpm.Nonce]byte:
|
||||
copy(t[:], nonce)
|
||||
default:
|
||||
return fmt.Errorf("unsupported target type for nonce validation: %T", target)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeAttestationRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*agent.AttestationRequest)
|
||||
var reportData [quoteprovider.Nonce]byte
|
||||
var nonce [vtpm.Nonce]byte
|
||||
|
||||
if len(req.TeeNonce) > quoteprovider.Nonce {
|
||||
return nil, ErrTEENonceLength
|
||||
if err := validateNonce(req.TeeNonce, quoteprovider.Nonce, &reportData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(req.VtpmNonce) > vtpm.Nonce {
|
||||
return nil, ErrVTpmNonceLength
|
||||
if err := validateNonce(req.VtpmNonce, vtpm.Nonce, &nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
copy(reportData[:], req.TeeNonce)
|
||||
copy(nonce[:], req.VtpmNonce)
|
||||
return attestationReq{TeeNonce: reportData, VtpmNonce: nonce, AttType: config.AttestationType(req.Type)}, nil
|
||||
return attestationReq{
|
||||
TeeNonce: reportData,
|
||||
VtpmNonce: nonce,
|
||||
AttType: attestation.PlatformType(req.Type),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeAttestationResponse(_ context.Context, response interface{}) (interface{}, error) {
|
||||
@@ -127,111 +181,262 @@ func encodeAttestationResponse(_ context.Context, response interface{}) (interfa
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Algo implements agent.AgentServiceServer.
|
||||
func (s *grpcServer) Algo(stream agent.AgentService_AlgoServer) error {
|
||||
var algoFile, reqFile []byte
|
||||
func decodeAttestationResultRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*agent.AttestationResultRequest)
|
||||
var nonce [vtpm.Nonce]byte
|
||||
|
||||
if err := validateNonce(req.TokenNonce, vtpm.Nonce, &nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return FetchAttestationResultReq{
|
||||
tokenNonce: nonce,
|
||||
AttType: attestation.PlatformType(req.Type),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeAttestationResultResponse(_ context.Context, response interface{}) (interface{}, error) {
|
||||
res := response.(fetchAttestationResultRes)
|
||||
return &agent.AttestationResultResponse{
|
||||
File: res.File,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeIMAMeasurementsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
return imaMeasurementsReq{}, nil
|
||||
}
|
||||
|
||||
func encodeIMAMeasurementsResponse(_ context.Context, response interface{}) (interface{}, error) {
|
||||
res := response.(imaMeasurementsRes)
|
||||
return &agent.IMAMeasurementsResponse{
|
||||
File: res.File,
|
||||
Pcr10: res.PCR10,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) streamingHandler(
|
||||
ctx context.Context,
|
||||
handlerName string,
|
||||
req interface{},
|
||||
stream interface{},
|
||||
sendFn func([]byte) error,
|
||||
getFileData func(interface{}) []byte,
|
||||
) error {
|
||||
handler, ok := s.handlers[handlerName]
|
||||
if !ok {
|
||||
return status.Errorf(codes.NotFound, "handler %q not found", handlerName)
|
||||
}
|
||||
|
||||
_, res, err := handler.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileData := getFileData(res)
|
||||
|
||||
// Set file size header
|
||||
if setter, ok := stream.(interface{ SetHeader(metadata.MD) error }); ok {
|
||||
if err := setter.SetHeader(metadata.New(map[string]string{
|
||||
FileSizeKey: fmt.Sprint(len(fileData)),
|
||||
})); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Stream the file data
|
||||
return s.streamFileData(bytes.NewBuffer(fileData), sendFn)
|
||||
}
|
||||
|
||||
func (s *grpcServer) streamFileData(buffer *bytes.Buffer, sendFn func([]byte) error) error {
|
||||
buf := make([]byte, bufferSize)
|
||||
for {
|
||||
algoChunk, err := stream.Recv()
|
||||
n, err := buffer.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
algoFile = append(algoFile, algoChunk.Algorithm...)
|
||||
reqFile = append(reqFile, algoChunk.Requirements...)
|
||||
|
||||
if err := sendFn(buf[:n]); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
_, res, err := s.algo.ServeGRPC(stream.Context(), &agent.AlgoRequest{Algorithm: algoFile, Requirements: reqFile})
|
||||
return nil
|
||||
}
|
||||
|
||||
func receiveStreamingData(getData func() ([]byte, string, error)) ([]byte, string, error) {
|
||||
var data []byte
|
||||
var filename string
|
||||
|
||||
for {
|
||||
chunk, fname, err := getData()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
data = append(data, chunk...)
|
||||
if fname != "" {
|
||||
filename = fname
|
||||
}
|
||||
}
|
||||
return data, filename, nil
|
||||
}
|
||||
|
||||
// Algo implements agent.AgentServiceServer.
|
||||
func (s *grpcServer) Algo(stream agent.AgentService_AlgoServer) error {
|
||||
algoFile, reqFile, err := s.receiveAlgoData(stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ar := res.(*agent.AlgoResponse)
|
||||
return stream.SendAndClose(ar)
|
||||
|
||||
_, res, err := s.handlers["algo"].ServeGRPC(stream.Context(), &agent.AlgoRequest{
|
||||
Algorithm: algoFile,
|
||||
Requirements: reqFile,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stream.SendAndClose(res.(*agent.AlgoResponse))
|
||||
}
|
||||
|
||||
func (s *grpcServer) receiveAlgoData(stream agent.AgentService_AlgoServer) ([]byte, []byte, error) {
|
||||
var algoFile, reqFile []byte
|
||||
for {
|
||||
chunk, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
algoFile = append(algoFile, chunk.Algorithm...)
|
||||
reqFile = append(reqFile, chunk.Requirements...)
|
||||
}
|
||||
return algoFile, reqFile, nil
|
||||
}
|
||||
|
||||
// Data implements agent.AgentServiceServer.
|
||||
func (s *grpcServer) Data(stream agent.AgentService_DataServer) error {
|
||||
var dataFile []byte
|
||||
var filename string
|
||||
for {
|
||||
dataChunk, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
dataFile, filename, err := receiveStreamingData(func() ([]byte, string, error) {
|
||||
chunk, err := stream.Recv()
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
return nil, "", err
|
||||
}
|
||||
dataFile = append(dataFile, dataChunk.Dataset...)
|
||||
filename = dataChunk.Filename
|
||||
}
|
||||
_, res, err := s.data.ServeGRPC(stream.Context(), &agent.DataRequest{Dataset: dataFile, Filename: filename})
|
||||
return chunk.Dataset, chunk.Filename, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ar := res.(*agent.DataResponse)
|
||||
return stream.SendAndClose(ar)
|
||||
|
||||
_, res, err := s.handlers["data"].ServeGRPC(stream.Context(), &agent.DataRequest{
|
||||
Dataset: dataFile,
|
||||
Filename: filename,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stream.SendAndClose(res.(*agent.DataResponse))
|
||||
}
|
||||
|
||||
func (s *grpcServer) Result(req *agent.ResultRequest, stream agent.AgentService_ResultServer) error {
|
||||
_, res, err := s.result.ServeGRPC(stream.Context(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr := res.(*agent.ResultResponse)
|
||||
|
||||
if err := stream.SetHeader(metadata.New(map[string]string{FileSizeKey: fmt.Sprint(len(rr.File))})); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
resultBuffer := bytes.NewBuffer(rr.File)
|
||||
|
||||
buf := make([]byte, bufferSize)
|
||||
|
||||
for {
|
||||
n, err := resultBuffer.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
if err := stream.Send(&agent.ResultResponse{File: buf[:n]}); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.streamingHandler(
|
||||
stream.Context(),
|
||||
"result",
|
||||
req,
|
||||
stream,
|
||||
func(data []byte) error {
|
||||
return stream.Send(&agent.ResultResponse{File: data})
|
||||
},
|
||||
func(res interface{}) []byte {
|
||||
return res.(*agent.ResultResponse).File
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *grpcServer) Attestation(req *agent.AttestationRequest, stream agent.AgentService_AttestationServer) error {
|
||||
_, res, err := s.attestation.ServeGRPC(stream.Context(), req)
|
||||
return s.streamingHandler(
|
||||
stream.Context(),
|
||||
"attestation",
|
||||
req,
|
||||
stream,
|
||||
func(data []byte) error {
|
||||
return stream.Send(&agent.AttestationResponse{File: data})
|
||||
},
|
||||
func(res interface{}) []byte {
|
||||
return res.(*agent.AttestationResponse).File
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *grpcServer) IMAMeasurements(req *agent.IMAMeasurementsRequest, stream agent.AgentService_IMAMeasurementsServer) error {
|
||||
_, res, err := s.handlers["imaMeasurements"].ServeGRPC(stream.Context(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr := res.(*agent.AttestationResponse)
|
||||
rr := res.(*agent.IMAMeasurementsResponse)
|
||||
|
||||
if err := stream.SetHeader(metadata.New(map[string]string{FileSizeKey: fmt.Sprint(len(rr.File))})); err != nil {
|
||||
if err := stream.SetHeader(metadata.New(map[string]string{
|
||||
FileSizeKey: strconv.Itoa(len(rr.File)),
|
||||
})); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
attestationBuffer := bytes.NewBuffer(rr.File)
|
||||
return s.streamDualBuffers(
|
||||
bytes.NewBuffer(rr.File),
|
||||
bytes.NewBuffer(rr.Pcr10),
|
||||
func(fileData, pcr10Data []byte) error {
|
||||
return stream.Send(&agent.IMAMeasurementsResponse{
|
||||
File: fileData,
|
||||
Pcr10: pcr10Data,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
buf := make([]byte, bufferSize)
|
||||
func (s *grpcServer) streamDualBuffers(
|
||||
buf1, buf2 *bytes.Buffer,
|
||||
sendFn func([]byte, []byte) error,
|
||||
) error {
|
||||
buff1 := make([]byte, bufferSize)
|
||||
buff2 := make([]byte, bufferSize)
|
||||
|
||||
for {
|
||||
n, err := attestationBuffer.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
n1, err1 := buf1.Read(buff1)
|
||||
if err1 != nil && err1 != io.EOF {
|
||||
return status.Error(codes.Internal, err1.Error())
|
||||
}
|
||||
|
||||
if err := stream.Send(&agent.AttestationResponse{File: buf[:n]}); err != nil {
|
||||
n2, err2 := buf2.Read(buff2)
|
||||
if err2 != nil && err2 != io.EOF {
|
||||
return status.Error(codes.Internal, err2.Error())
|
||||
}
|
||||
|
||||
if n1 == 0 && err1 == io.EOF && n2 == 0 && err2 == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err := sendFn(buff1[:n1], buff2[:n2]); err != nil {
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) AttestationResult(ctx context.Context, req *agent.AttestationResultRequest) (*agent.AttestationResultResponse, error) {
|
||||
_, res, err := s.handlers["attestationResult"].ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rr, ok := res.(*agent.AttestationResultResponse)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "failed to cast response to AttestationResultResponse")
|
||||
}
|
||||
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
+303
-18
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"github.com/ultravioletrs/cocos/agent/mocks"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"google.golang.org/grpc"
|
||||
@@ -68,8 +68,9 @@ func (m *MockAgentService_ResultServer) Context() context.Context {
|
||||
return m.ctx
|
||||
}
|
||||
|
||||
func (m *MockAgentService_ResultServer) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
func (m *MockAgentService_ResultServer) SetHeader(md metadata.MD) error {
|
||||
args := m.Called(md)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockAgentService_ResultServer) Send(resp *agent.ResultResponse) error {
|
||||
@@ -92,8 +93,46 @@ func (m *MockAgentService_AttestationServer) Send(resp *agent.AttestationRespons
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockAgentService_AttestationServer) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
func (m *MockAgentService_AttestationServer) SetHeader(md metadata.MD) error {
|
||||
args := m.Called(md)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
type MockAgentService_IMAMeasurementsServer struct {
|
||||
grpc.ServerStream
|
||||
mock.Mock
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (m *MockAgentService_IMAMeasurementsServer) Context() context.Context {
|
||||
return m.ctx
|
||||
}
|
||||
|
||||
func (m *MockAgentService_IMAMeasurementsServer) Send(resp *agent.IMAMeasurementsResponse) error {
|
||||
args := m.Called(resp)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockAgentService_IMAMeasurementsServer) SetHeader(md metadata.MD) error {
|
||||
args := m.Called(md)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
grpcServer, ok := server.(*grpcServer)
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, grpcServer.handlers)
|
||||
assert.Len(t, grpcServer.handlers, 6) // Should have 6 handlers
|
||||
|
||||
// Check that all expected handlers are present
|
||||
expectedHandlers := []string{"algo", "data", "result", "attestation", "imaMeasurements", "attestationResult"}
|
||||
for _, handler := range expectedHandlers {
|
||||
assert.Contains(t, grpcServer.handlers, handler)
|
||||
assert.NotNil(t, grpcServer.handlers[handler])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlgo(t *testing.T) {
|
||||
@@ -102,8 +141,8 @@ func TestAlgo(t *testing.T) {
|
||||
|
||||
mockStream := &MockAgentService_AlgoServer{ctx: context.Background()}
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{Algorithm: []byte("algo"), Requirements: []byte("req")}, nil).Once()
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{}, io.EOF)
|
||||
mockStream.On("SendAndClose", &agent.AlgoResponse{}).Return(nil)
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{}, io.EOF).Once()
|
||||
mockStream.On("SendAndClose", &agent.AlgoResponse{}).Return(nil).Once()
|
||||
|
||||
mockService.On("Algo", context.Background(), agent.Algorithm{Algorithm: []byte("algo"), Requirements: []byte("req")}).Return(nil)
|
||||
|
||||
@@ -114,14 +153,33 @@ func TestAlgo(t *testing.T) {
|
||||
mockService.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestAlgoWithMultipleChunks(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
mockStream := &MockAgentService_AlgoServer{ctx: context.Background()}
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{Algorithm: []byte("algo"), Requirements: []byte("req")}, nil).Once()
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{Algorithm: []byte("2"), Requirements: []byte("2")}, nil).Once()
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{}, io.EOF).Once()
|
||||
mockStream.On("SendAndClose", &agent.AlgoResponse{}).Return(nil).Once()
|
||||
|
||||
mockService.On("Algo", context.Background(), agent.Algorithm{Algorithm: []byte("algo2"), Requirements: []byte("req2")}).Return(nil)
|
||||
|
||||
err := server.Algo(mockStream)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockStream.AssertExpectations(t)
|
||||
mockService.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestData(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
mockStream := &MockAgentService_DataServer{ctx: context.Background()}
|
||||
mockStream.On("Recv").Return(&agent.DataRequest{Dataset: []byte("data"), Filename: "test.txt"}, nil).Once()
|
||||
mockStream.On("Recv").Return(&agent.DataRequest{}, io.EOF)
|
||||
mockStream.On("SendAndClose", &agent.DataResponse{}).Return(nil)
|
||||
mockStream.On("Recv").Return(&agent.DataRequest{}, io.EOF).Once()
|
||||
mockStream.On("SendAndClose", &agent.DataResponse{}).Return(nil).Once()
|
||||
|
||||
mockService.On("Data", context.Background(), agent.Dataset{Dataset: []byte("data"), Filename: "test.txt"}).Return(nil)
|
||||
|
||||
@@ -136,9 +194,18 @@ func TestResult(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
resultData := []byte("result data")
|
||||
mockStream := &MockAgentService_ResultServer{ctx: context.Background()}
|
||||
mockService.On("Result", mock.Anything).Return([]byte("result data"), nil)
|
||||
mockStream.On("Send", mock.AnythingOfType("*agent.ResultResponse")).Return(nil)
|
||||
|
||||
// Mock the SetHeader call
|
||||
mockStream.On("SetHeader", mock.AnythingOfType("metadata.MD")).Return(nil).Once()
|
||||
|
||||
// Mock the Send call - it should be called with the result data
|
||||
mockStream.On("Send", mock.MatchedBy(func(resp *agent.ResultResponse) bool {
|
||||
return len(resp.File) > 0
|
||||
})).Return(nil).Once()
|
||||
|
||||
mockService.On("Result", mock.Anything).Return(resultData, nil)
|
||||
|
||||
err := server.Result(&agent.ResultRequest{}, mockStream)
|
||||
assert.NoError(t, err)
|
||||
@@ -151,18 +218,135 @@ func TestAttestation(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
attestationData := []byte("attestation data")
|
||||
mockStream := &MockAgentService_AttestationServer{ctx: context.Background()}
|
||||
mockStream.On("Send", mock.AnythingOfType("*agent.AttestationResponse")).Return(nil)
|
||||
|
||||
// Mock the SetHeader call
|
||||
mockStream.On("SetHeader", mock.AnythingOfType("metadata.MD")).Return(nil).Once()
|
||||
|
||||
// Mock the Send call
|
||||
mockStream.On("Send", mock.MatchedBy(func(resp *agent.AttestationResponse) bool {
|
||||
return len(resp.File) > 0
|
||||
})).Return(nil).Once()
|
||||
|
||||
reportData := [quoteprovider.Nonce]byte{}
|
||||
vtpmNonce := [vtpm.Nonce]byte{}
|
||||
attestationType := config.SNP
|
||||
mockService.On("Attestation", mock.Anything, reportData, vtpmNonce, attestationType).Return([]byte("attestation data"), nil)
|
||||
attestationType := attestation.SNP
|
||||
mockService.On("Attestation", mock.Anything, reportData, vtpmNonce, attestationType).Return(attestationData, nil)
|
||||
|
||||
err := server.Attestation(&agent.AttestationRequest{TeeNonce: reportData[:]}, mockStream)
|
||||
err := server.Attestation(&agent.AttestationRequest{TeeNonce: reportData[:], Type: int32(attestationType)}, mockStream)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
mockStream.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestIMAMeasurements(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
imaData := []byte("ima data")
|
||||
pcr10Data := []byte("pcr10 data")
|
||||
|
||||
mockStream := &MockAgentService_IMAMeasurementsServer{ctx: context.Background()}
|
||||
|
||||
// Mock the SetHeader call
|
||||
mockStream.On("SetHeader", mock.AnythingOfType("metadata.MD")).Return(nil).Once()
|
||||
|
||||
// Mock the Send call
|
||||
mockStream.On("Send", mock.MatchedBy(func(resp *agent.IMAMeasurementsResponse) bool {
|
||||
return len(resp.File) > 0 || len(resp.Pcr10) > 0
|
||||
})).Return(nil).Once()
|
||||
|
||||
mockService.On("IMAMeasurements", mock.Anything).Return(imaData, pcr10Data, nil)
|
||||
|
||||
err := server.IMAMeasurements(&agent.IMAMeasurementsRequest{}, mockStream)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
mockStream.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestAttestationResult(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
attestationData := []byte("attestation result data")
|
||||
vtpmNonce := [vtpm.Nonce]byte{}
|
||||
attestationType := attestation.SNP
|
||||
|
||||
mockService.On("AttestationResult", mock.Anything, vtpmNonce, attestationType).Return(attestationData, nil)
|
||||
|
||||
resp, err := server.AttestationResult(context.Background(), &agent.AttestationResultRequest{
|
||||
TokenNonce: vtpmNonce[:],
|
||||
Type: int32(attestationType),
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, attestationData, resp.File)
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestValidateNonce(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nonce []byte
|
||||
maxLen int
|
||||
shouldError bool
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "valid TEE nonce",
|
||||
nonce: make([]byte, quoteprovider.Nonce),
|
||||
maxLen: quoteprovider.Nonce,
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
name: "valid vTPM nonce",
|
||||
nonce: make([]byte, vtpm.Nonce),
|
||||
maxLen: vtpm.Nonce,
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
name: "TEE nonce too long",
|
||||
nonce: make([]byte, quoteprovider.Nonce+1),
|
||||
maxLen: quoteprovider.Nonce,
|
||||
shouldError: true,
|
||||
expectedErr: ErrTEENonceLength,
|
||||
},
|
||||
{
|
||||
name: "vTPM nonce too long",
|
||||
nonce: make([]byte, vtpm.Nonce+1),
|
||||
maxLen: vtpm.Nonce,
|
||||
shouldError: true,
|
||||
expectedErr: ErrVTPMNonceLength,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.maxLen == quoteprovider.Nonce {
|
||||
var target [quoteprovider.Nonce]byte
|
||||
err := validateNonce(tt.nonce, tt.maxLen, &target)
|
||||
if tt.shouldError {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
} else {
|
||||
var target [vtpm.Nonce]byte
|
||||
err := validateNonce(tt.nonce, tt.maxLen, &target)
|
||||
if tt.shouldError {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeAlgoRequest(t *testing.T) {
|
||||
@@ -204,11 +388,38 @@ func TestEncodeResultResponse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecodeAttestationRequest(t *testing.T) {
|
||||
nonce := [quoteprovider.Nonce]byte{}
|
||||
req := &agent.AttestationRequest{TeeNonce: nonce[:]}
|
||||
teeNonce := make([]byte, quoteprovider.Nonce)
|
||||
vtpmNonce := make([]byte, vtpm.Nonce)
|
||||
|
||||
req := &agent.AttestationRequest{
|
||||
TeeNonce: teeNonce,
|
||||
VtpmNonce: vtpmNonce,
|
||||
Type: int32(attestation.SNP),
|
||||
}
|
||||
|
||||
decoded, err := decodeAttestationRequest(context.Background(), req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, attestationReq{TeeNonce: nonce}, decoded)
|
||||
|
||||
decodedReq := decoded.(attestationReq)
|
||||
assert.Equal(t, attestation.SNP, decodedReq.AttType)
|
||||
}
|
||||
|
||||
func TestDecodeAttestationRequestWithInvalidNonce(t *testing.T) {
|
||||
// Test with TEE nonce too long
|
||||
teeNonce := make([]byte, quoteprovider.Nonce+1)
|
||||
req := &agent.AttestationRequest{TeeNonce: teeNonce}
|
||||
|
||||
_, err := decodeAttestationRequest(context.Background(), req)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, ErrTEENonceLength, err)
|
||||
|
||||
// Test with vTPM nonce too long
|
||||
vtpmNonce := make([]byte, vtpm.Nonce+1)
|
||||
req = &agent.AttestationRequest{VtpmNonce: vtpmNonce}
|
||||
|
||||
_, err = decodeAttestationRequest(context.Background(), req)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, ErrVTPMNonceLength, err)
|
||||
}
|
||||
|
||||
func TestEncodeAttestationResponse(t *testing.T) {
|
||||
@@ -216,3 +427,77 @@ func TestEncodeAttestationResponse(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &agent.AttestationResponse{File: []byte("attestation")}, encoded)
|
||||
}
|
||||
|
||||
func TestDecodeAttestationResultRequest(t *testing.T) {
|
||||
tokenNonce := make([]byte, vtpm.Nonce)
|
||||
req := &agent.AttestationResultRequest{
|
||||
TokenNonce: tokenNonce,
|
||||
Type: int32(attestation.SNP),
|
||||
}
|
||||
|
||||
decoded, err := decodeAttestationResultRequest(context.Background(), req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
decodedReq := decoded.(FetchAttestationResultReq)
|
||||
assert.Equal(t, attestation.SNP, decodedReq.AttType)
|
||||
}
|
||||
|
||||
func TestDecodeAttestationResultRequestWithInvalidNonce(t *testing.T) {
|
||||
// Test with token nonce too long
|
||||
tokenNonce := make([]byte, vtpm.Nonce+1)
|
||||
req := &agent.AttestationResultRequest{TokenNonce: tokenNonce}
|
||||
|
||||
_, err := decodeAttestationResultRequest(context.Background(), req)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, ErrVTPMNonceLength, err)
|
||||
}
|
||||
|
||||
func TestEncodeAttestationResultResponse(t *testing.T) {
|
||||
encoded, err := encodeAttestationResultResponse(context.Background(), fetchAttestationResultRes{File: []byte("attestation")})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &agent.AttestationResultResponse{File: []byte("attestation")}, encoded)
|
||||
}
|
||||
|
||||
func TestDecodeIMAMeasurementsRequest(t *testing.T) {
|
||||
decoded, err := decodeIMAMeasurementsRequest(context.Background(), &agent.IMAMeasurementsRequest{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, imaMeasurementsReq{}, decoded)
|
||||
}
|
||||
|
||||
func TestEncodeIMAMeasurementsResponse(t *testing.T) {
|
||||
encoded, err := encodeIMAMeasurementsResponse(context.Background(), imaMeasurementsRes{
|
||||
File: []byte("ima"),
|
||||
PCR10: []byte("pcr10"),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &agent.IMAMeasurementsResponse{
|
||||
File: []byte("ima"),
|
||||
Pcr10: []byte("pcr10"),
|
||||
}, encoded)
|
||||
}
|
||||
|
||||
func TestAlgoWithStreamError(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
mockStream := &MockAgentService_AlgoServer{ctx: context.Background()}
|
||||
mockStream.On("Recv").Return(&agent.AlgoRequest{}, assert.AnError).Once()
|
||||
|
||||
err := server.Algo(mockStream)
|
||||
assert.Error(t, err)
|
||||
|
||||
mockStream.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestDataWithStreamError(t *testing.T) {
|
||||
mockService := new(mocks.Service)
|
||||
server := NewServer(mockService)
|
||||
|
||||
mockStream := &MockAgentService_DataServer{ctx: context.Background()}
|
||||
mockStream.On("Recv").Return(&agent.DataRequest{}, assert.AnError).Once()
|
||||
|
||||
err := server.Data(mockStream)
|
||||
assert.Error(t, err)
|
||||
|
||||
mockStream.AssertExpectations(t)
|
||||
}
|
||||
|
||||
+29
-3
@@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
@@ -34,7 +34,7 @@ func LoggingMiddleware(svc agent.Service, logger *slog.Logger) agent.Service {
|
||||
func (lm *loggingMiddleware) State() (state string) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method State took %s to complete with state %s", time.Since(begin), state)
|
||||
lm.logger.Info(message)
|
||||
lm.logger.Debug(message)
|
||||
}(time.Now())
|
||||
return lm.svc.State()
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (lm *loggingMiddleware) Result(ctx context.Context) (response []byte, err e
|
||||
return lm.svc.Result(ctx)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType config.AttestationType) (response []byte, err error) {
|
||||
func (lm *loggingMiddleware) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) (response []byte, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method Attestation took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
@@ -118,3 +118,29 @@ func (lm *loggingMiddleware) Attestation(ctx context.Context, reportData [quotep
|
||||
|
||||
return lm.svc.Attestation(ctx, reportData, nonce, attType)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) IMAMeasurements(ctx context.Context) (file []byte, pcr10 []byte, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method IMAMeasurements took %s to complete", 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.IMAMeasurements(ctx)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) AttestationResult(ctx context.Context, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) (response []byte, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method AttestationResult took %s to complete", 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.AttestationResult(ctx, nonce, attType)
|
||||
}
|
||||
|
||||
+20
-2
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
@@ -92,7 +92,7 @@ func (ms *metricsMiddleware) Result(ctx context.Context) ([]byte, error) {
|
||||
return ms.svc.Result(ctx)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType config.AttestationType) ([]byte, error) {
|
||||
func (ms *metricsMiddleware) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "attestation").Add(1)
|
||||
ms.latency.With("method", "attestation").Observe(time.Since(begin).Seconds())
|
||||
@@ -100,3 +100,21 @@ func (ms *metricsMiddleware) Attestation(ctx context.Context, reportData [quotep
|
||||
|
||||
return ms.svc.Attestation(ctx, reportData, nonce, attType)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) AttestationResult(ctx context.Context, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "attestation_result").Add(1)
|
||||
ms.latency.With("method", "attestation_result").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.AttestationResult(ctx, nonce, attType)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) IMAMeasurements(ctx context.Context) ([]byte, []byte, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "imameasurements").Add(1)
|
||||
ms.latency.With("method", "imameasurements").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.IMAMeasurements(ctx)
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
var _ fmt.Stringer = (*Datasets)(nil)
|
||||
|
||||
type AgentConfig struct {
|
||||
Host string `json:"host,omitempty"`
|
||||
Port string `json:"port,omitempty"`
|
||||
CertFile string `json:"cert_file,omitempty"`
|
||||
KeyFile string `json:"server_key,omitempty"`
|
||||
|
||||
@@ -106,7 +106,6 @@ func TestDecompressToContext(t *testing.T) {
|
||||
|
||||
func TestAgentConfigJSON(t *testing.T) {
|
||||
config := AgentConfig{
|
||||
Host: "localhost",
|
||||
Port: "8080",
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
|
||||
@@ -4,15 +4,19 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms/api/grpc/storage"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms/server"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
pkggrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -42,11 +46,12 @@ type CVMSClient struct {
|
||||
runReqManager *runRequestManager
|
||||
sp server.AgentServer
|
||||
storage storage.Storage
|
||||
reconnectFn func(context.Context) (cvms.Service_ProcessClient, error)
|
||||
reconnectFn func(context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error)
|
||||
grpcClient pkggrpc.Client
|
||||
}
|
||||
|
||||
// NewClient returns new gRPC client instance.
|
||||
func NewClient(stream cvms.Service_ProcessClient, svc agent.Service, messageQueue chan *cvms.ClientStreamMessage, logger *slog.Logger, sp server.AgentServer, storageDir string, reconnectFn func(context.Context) (cvms.Service_ProcessClient, error)) (*CVMSClient, error) {
|
||||
func NewClient(stream cvms.Service_ProcessClient, svc agent.Service, messageQueue chan *cvms.ClientStreamMessage, logger *slog.Logger, sp server.AgentServer, storageDir string, reconnectFn func(context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error), grpcClient pkggrpc.Client) (*CVMSClient, error) {
|
||||
store, err := storage.NewFileStorage(storageDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,6 +66,7 @@ func NewClient(stream cvms.Service_ProcessClient, svc agent.Service, messageQueu
|
||||
sp: sp,
|
||||
storage: store,
|
||||
reconnectFn: reconnectFn,
|
||||
grpcClient: grpcClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -74,7 +80,7 @@ func (client *CVMSClient) Process(ctx context.Context, cancel context.CancelFunc
|
||||
client.logger.Info("Connection lost, attempting to reconnect...", "error", err)
|
||||
time.Sleep(reconnectInterval)
|
||||
|
||||
stream, err := client.reconnectFn(ctx)
|
||||
grpcClient, stream, err := client.reconnectFn(ctx)
|
||||
if err != nil {
|
||||
client.logger.Error("Failed to reconnect", "error", err)
|
||||
continue
|
||||
@@ -82,6 +88,7 @@ func (client *CVMSClient) Process(ctx context.Context, cancel context.CancelFunc
|
||||
|
||||
client.mu.Lock()
|
||||
client.stream = stream
|
||||
client.grpcClient = grpcClient
|
||||
client.mu.Unlock()
|
||||
}
|
||||
}
|
||||
@@ -172,6 +179,13 @@ func (client *CVMSClient) processIncomingMessage(ctx context.Context, req *cvms.
|
||||
go client.handleStopComputation(ctx, mes)
|
||||
case *cvms.ServerStreamMessage_AgentStateReq:
|
||||
client.handleAgentStateReq(mes)
|
||||
case *cvms.ServerStreamMessage_DisconnectReq:
|
||||
client.logger.Info("Received disconnect request")
|
||||
client.mu.Lock()
|
||||
if err := client.grpcClient.Close(); err != nil {
|
||||
client.logger.Error("Failed to close gRPC client", "error", err)
|
||||
}
|
||||
client.mu.Unlock()
|
||||
default:
|
||||
return errUnknonwMessageType
|
||||
}
|
||||
@@ -238,6 +252,8 @@ func (client *CVMSClient) executeRun(ctx context.Context, runReq *cvms.Computati
|
||||
return
|
||||
}
|
||||
|
||||
ccPlatform := attestation.CCPlatform()
|
||||
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
|
||||
@@ -253,7 +269,6 @@ func (client *CVMSClient) executeRun(ctx context.Context, runReq *cvms.Computati
|
||||
|
||||
if err := client.sp.Start(agent.AgentConfig{
|
||||
Port: runReq.AgentConfig.Port,
|
||||
Host: runReq.AgentConfig.Host,
|
||||
CertFile: runReq.AgentConfig.CertFile,
|
||||
KeyFile: runReq.AgentConfig.KeyFile,
|
||||
ServerCAFile: runReq.AgentConfig.ServerCaFile,
|
||||
@@ -264,6 +279,20 @@ func (client *CVMSClient) executeRun(ctx context.Context, runReq *cvms.Computati
|
||||
runRes.RunRes.Error = err.Error()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if ccPlatform == attestation.Azure || ccPlatform == attestation.SNPvTPM {
|
||||
cmpJson, err := json.Marshal(ac)
|
||||
if err != nil {
|
||||
client.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
if err = vtpm.ExtendPCR(vtpm.PCR16, cmpJson); err != nil {
|
||||
client.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
client.sendMessage(&cvms.ClientStreamMessage{Message: runRes})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
mglog "github.com/absmach/magistrala/logger"
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
servermocks "github.com/ultravioletrs/cocos/agent/cvms/server/mocks"
|
||||
"github.com/ultravioletrs/cocos/agent/mocks"
|
||||
pkggrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
clientmocks "github.com/ultravioletrs/cocos/pkg/clients/grpc/mocks"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -35,13 +38,13 @@ func (m *mockStream) Send(msg *cvms.ClientStreamMessage) error {
|
||||
func TestManagerClient_Process(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupMocks func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer)
|
||||
setupMocks func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client)
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "Stop computation",
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer) {
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client) {
|
||||
mockStream.On("Recv").Return(&cvms.ServerStreamMessage{
|
||||
Message: &cvms.ServerStreamMessage_StopComputation{
|
||||
StopComputation: &cvms.StopComputation{},
|
||||
@@ -56,7 +59,7 @@ func TestManagerClient_Process(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Run request chunks",
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer) {
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client) {
|
||||
mockStream.On("Recv").Return(&cvms.ServerStreamMessage{
|
||||
Message: &cvms.ServerStreamMessage_RunReqChunks{
|
||||
RunReqChunks: &cvms.RunReqChunks{},
|
||||
@@ -67,9 +70,37 @@ func TestManagerClient_Process(t *testing.T) {
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Agent state request",
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client) {
|
||||
mockStream.On("Recv").Return(&cvms.ServerStreamMessage{
|
||||
Message: &cvms.ServerStreamMessage_AgentStateReq{
|
||||
AgentStateReq: &cvms.AgentStateReq{
|
||||
Id: "test-agent",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
mockStream.On("Send", mock.Anything).Return(nil)
|
||||
mockSvc.On("State").Return("test-state")
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "context deadline exceeded",
|
||||
},
|
||||
{
|
||||
name: "Disconnect request",
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client) {
|
||||
mockStream.On("Recv").Return(&cvms.ServerStreamMessage{
|
||||
Message: &cvms.ServerStreamMessage_DisconnectReq{},
|
||||
}, nil)
|
||||
mockStream.On("Send", mock.Anything).Return(nil)
|
||||
grpcClient.On("Close").Return(nil)
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "context deadline exceeded",
|
||||
},
|
||||
{
|
||||
name: "Receive error",
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer) {
|
||||
setupMocks: func(mockStream *mockStream, mockSvc *mocks.Service, mockServerSvc *servermocks.AgentServer, grpcClient *clientmocks.Client) {
|
||||
mockStream.On("Recv").Return(&cvms.ServerStreamMessage{}, assert.AnError)
|
||||
},
|
||||
expectError: true,
|
||||
@@ -88,13 +119,15 @@ func TestManagerClient_Process(t *testing.T) {
|
||||
<-messageQueue
|
||||
}()
|
||||
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (cvms.Service_ProcessClient, error) { return nil, nil })
|
||||
grpcClient := new(clientmocks.Client)
|
||||
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) { return nil, nil, nil }, grpcClient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
tc.setupMocks(mockStream, mockSvc, mockServerSvc)
|
||||
tc.setupMocks(mockStream, mockSvc, mockServerSvc, grpcClient)
|
||||
|
||||
err = client.Process(ctx, cancel)
|
||||
|
||||
@@ -116,12 +149,26 @@ func TestManagerClient_handleRunReqChunks(t *testing.T) {
|
||||
mockServerSvc := new(servermocks.AgentServer)
|
||||
messageQueue := make(chan *cvms.ClientStreamMessage, 10)
|
||||
logger := mglog.NewMock()
|
||||
grpcClient := new(clientmocks.Client)
|
||||
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (cvms.Service_ProcessClient, error) { return nil, nil })
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) { return nil, nil, nil }, grpcClient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
runReq := &cvms.ComputationRunReq{
|
||||
Id: "test-id",
|
||||
Datasets: []*cvms.Dataset{
|
||||
{
|
||||
Hash: sha3.New256().Sum([]byte("test-dataset")),
|
||||
},
|
||||
},
|
||||
Algorithm: &cvms.Algorithm{
|
||||
Hash: sha3.New256().Sum([]byte("test-algorithm")),
|
||||
},
|
||||
ResultConsumers: []*cvms.ResultConsumer{
|
||||
{
|
||||
UserKey: []byte("test-consumer"),
|
||||
},
|
||||
},
|
||||
}
|
||||
runReqBytes, _ := proto.Marshal(runReq)
|
||||
|
||||
@@ -167,8 +214,9 @@ func TestManagerClient_handleStopComputation(t *testing.T) {
|
||||
mockServerSvc := new(servermocks.AgentServer)
|
||||
messageQueue := make(chan *cvms.ClientStreamMessage, 10)
|
||||
logger := mglog.NewMock()
|
||||
grpcClient := new(clientmocks.Client)
|
||||
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (cvms.Service_ProcessClient, error) { return nil, nil })
|
||||
client, err := NewClient(mockStream, mockSvc, messageQueue, logger, mockServerSvc, t.TempDir(), func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) { return nil, nil, nil }, grpcClient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
stopReq := &cvms.ServerStreamMessage_StopComputation{
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -71,7 +71,7 @@ func (_c *Storage_Add_Call) RunAndReturn(run func(*cvms.ClientStreamMessage) err
|
||||
return _c
|
||||
}
|
||||
|
||||
// Clear provides a mock function with given fields:
|
||||
// Clear provides a mock function with no fields
|
||||
func (_m *Storage) Clear() error {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -116,7 +116,7 @@ func (_c *Storage_Clear_Call) RunAndReturn(run func() error) *Storage_Clear_Call
|
||||
return _c
|
||||
}
|
||||
|
||||
// Load provides a mock function with given fields:
|
||||
// Load provides a mock function with no fields
|
||||
func (_m *Storage) Load() ([]storage.Message, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package storage
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
)
|
||||
|
||||
func createTempDir(t *testing.T) string {
|
||||
tmpDir, err := os.MkdirTemp("", "storage_test_*")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
os.RemoveAll(tmpDir)
|
||||
})
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
func createTestMessage(content string) *cvms.ClientStreamMessage {
|
||||
return &cvms.ClientStreamMessage{
|
||||
Message: &cvms.ClientStreamMessage_RunRes{
|
||||
RunRes: &cvms.RunResponse{
|
||||
Error: "",
|
||||
ComputationId: content,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFileStorage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
storageDir string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid directory",
|
||||
storageDir: createTempDir(t),
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "non-existent directory gets created",
|
||||
storageDir: filepath.Join(createTempDir(t), "subdir"),
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid directory path",
|
||||
storageDir: "/invalid/path/that/cannot/be/created",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
storage, err := NewFileStorage(tt.storageDir)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, storage)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, storage)
|
||||
assert.Equal(t, filepath.Join(tt.storageDir, "pending_messages.json"), storage.path)
|
||||
assert.Empty(t, storage.msgs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStorage_Load(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFile func(string) error
|
||||
expectedMsgs int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "load from non-existent file",
|
||||
setupFile: func(path string) error {
|
||||
// Don't create file
|
||||
return nil
|
||||
},
|
||||
expectedMsgs: 0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "load from empty file",
|
||||
setupFile: func(path string) error {
|
||||
return os.WriteFile(path, []byte("[]"), 0o644)
|
||||
},
|
||||
expectedMsgs: 0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "load from corrupted file",
|
||||
setupFile: func(path string) error {
|
||||
return os.WriteFile(path, []byte("invalid json"), 0o644)
|
||||
},
|
||||
expectedMsgs: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = tt.setupFile(storage.path)
|
||||
require.NoError(t, err)
|
||||
|
||||
msgs, err := storage.Load()
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, msgs, tt.expectedMsgs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStorage_Save(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
messages []Message
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "save empty messages",
|
||||
messages: []Message{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "save single message",
|
||||
messages: []Message{
|
||||
{
|
||||
Message: createTestMessage("test"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "save multiple messages",
|
||||
messages: []Message{
|
||||
{
|
||||
Message: createTestMessage("test1"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
{
|
||||
Message: createTestMessage("test2"),
|
||||
Time: time.Now().Add(time.Second),
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = storage.Save(tt.messages)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify file was written correctly
|
||||
_, err := os.ReadFile(storage.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify internal state was updated
|
||||
assert.Equal(t, tt.messages, storage.msgs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStorage_Add(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialMsgs []Message
|
||||
newMessage *cvms.ClientStreamMessage
|
||||
expectError bool
|
||||
expectedCount int
|
||||
}{
|
||||
{
|
||||
name: "add to empty storage",
|
||||
initialMsgs: []Message{},
|
||||
newMessage: createTestMessage("new"),
|
||||
expectError: false,
|
||||
expectedCount: 1,
|
||||
},
|
||||
{
|
||||
name: "add to existing messages",
|
||||
initialMsgs: []Message{
|
||||
{
|
||||
Message: createTestMessage("existing"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
},
|
||||
newMessage: createTestMessage("new"),
|
||||
expectError: false,
|
||||
expectedCount: 2,
|
||||
},
|
||||
{
|
||||
name: "add nil message",
|
||||
initialMsgs: []Message{},
|
||||
newMessage: nil,
|
||||
expectError: false,
|
||||
expectedCount: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup initial messages
|
||||
if len(tt.initialMsgs) > 0 {
|
||||
err = storage.Save(tt.initialMsgs)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
beforeTime := time.Now()
|
||||
err = storage.Add(tt.newMessage)
|
||||
afterTime := time.Now()
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify message was added to internal state
|
||||
assert.Len(t, storage.msgs, tt.expectedCount)
|
||||
|
||||
// Verify timestamp is reasonable
|
||||
if tt.expectedCount > 0 {
|
||||
lastMsg := storage.msgs[len(storage.msgs)-1]
|
||||
assert.True(t, lastMsg.Time.After(beforeTime) || lastMsg.Time.Equal(beforeTime))
|
||||
assert.True(t, lastMsg.Time.Before(afterTime) || lastMsg.Time.Equal(afterTime))
|
||||
assert.Equal(t, tt.newMessage, lastMsg.Message)
|
||||
}
|
||||
|
||||
_, err := os.ReadFile(storage.path)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStorage_Clear(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialMsgs []Message
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "clear empty storage",
|
||||
initialMsgs: []Message{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "clear storage with messages",
|
||||
initialMsgs: []Message{
|
||||
{
|
||||
Message: createTestMessage("test1"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
{
|
||||
Message: createTestMessage("test2"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup initial messages
|
||||
if len(tt.initialMsgs) > 0 {
|
||||
err = storage.Save(tt.initialMsgs)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err = storage.Clear()
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify internal state is cleared
|
||||
assert.Empty(t, storage.msgs)
|
||||
|
||||
// Verify file contains empty array
|
||||
data, err := os.ReadFile(storage.path)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "[]", string(data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileStorage_ConcurrentAccess(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test concurrent Add operations
|
||||
numGoroutines := 10
|
||||
done := make(chan bool, numGoroutines)
|
||||
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
go func(id int) {
|
||||
defer func() { done <- true }()
|
||||
|
||||
msg := createTestMessage(string(rune('A' + id)))
|
||||
err := storage.Add(msg)
|
||||
assert.NoError(t, err)
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait for all goroutines to complete
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Verify all messages were added
|
||||
msgs, err := storage.Load()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, msgs, numGoroutines)
|
||||
}
|
||||
|
||||
func TestFileStorage_IntegrationFlow(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test full workflow
|
||||
|
||||
// 1. Load from empty storage
|
||||
msgs, err := storage.Load()
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, msgs)
|
||||
|
||||
// 2. Add some messages
|
||||
msg1 := createTestMessage("message1")
|
||||
err = storage.Add(msg1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
msg2 := createTestMessage("message2")
|
||||
err = storage.Add(msg2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 3. Load and verify
|
||||
msgs, err = storage.Load()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, msgs, 2)
|
||||
|
||||
// 4. Save new set of messages
|
||||
newMsgs := []Message{
|
||||
{
|
||||
Message: createTestMessage("new1"),
|
||||
Time: time.Now(),
|
||||
},
|
||||
}
|
||||
err = storage.Save(newMsgs)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 5. Load and verify replacement
|
||||
msgs, err = storage.Load()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, msgs, 1)
|
||||
|
||||
// 6. Clear storage
|
||||
err = storage.Clear()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 7. Verify empty
|
||||
msgs, err = storage.Load()
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, msgs)
|
||||
}
|
||||
|
||||
func TestFileStorage_FilePermissions(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add a message to create the file
|
||||
msg := createTestMessage("test")
|
||||
err = storage.Add(msg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check file permissions
|
||||
info, err := os.Stat(storage.path)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, os.FileMode(0o644), info.Mode().Perm())
|
||||
}
|
||||
|
||||
func TestFileStorage_ErrorHandling(t *testing.T) {
|
||||
tmpDir := createTempDir(t)
|
||||
storage, err := NewFileStorage(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make directory read-only to trigger write errors
|
||||
err = os.Chmod(tmpDir, 0o555)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Restore permissions for cleanup
|
||||
t.Cleanup(func() {
|
||||
if err := os.Chmod(tmpDir, 0o755); err != nil {
|
||||
t.Errorf("Failed to restore permissions: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
// Try to add a message - should fail due to write permissions
|
||||
msg := createTestMessage("test")
|
||||
err = storage.Add(msg)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Try to save - should fail due to write permissions
|
||||
err = storage.Save([]Message{})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Try to clear - should fail due to write permissions
|
||||
err = storage.Clear()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
+374
-150
@@ -3,7 +3,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.0
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc v5.29.0
|
||||
// source: agent/cvms/cvms.proto
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -429,6 +430,8 @@ type ClientStreamMessage struct {
|
||||
// *ClientStreamMessage_RunRes
|
||||
// *ClientStreamMessage_StopComputationRes
|
||||
// *ClientStreamMessage_AgentStateRes
|
||||
// *ClientStreamMessage_VTPMattestationReport
|
||||
// *ClientStreamMessage_AzureAttestationResult
|
||||
Message isClientStreamMessage_Message `protobuf_oneof:"message"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -516,6 +519,24 @@ func (x *ClientStreamMessage) GetAgentStateRes() *AgentStateRes {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientStreamMessage) GetVTPMattestationReport() *AttestationResponse {
|
||||
if x != nil {
|
||||
if x, ok := x.Message.(*ClientStreamMessage_VTPMattestationReport); ok {
|
||||
return x.VTPMattestationReport
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientStreamMessage) GetAzureAttestationResult() *AzureAttestationResponse {
|
||||
if x != nil {
|
||||
if x, ok := x.Message.(*ClientStreamMessage_AzureAttestationResult); ok {
|
||||
return x.AzureAttestationResult
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isClientStreamMessage_Message interface {
|
||||
isClientStreamMessage_Message()
|
||||
}
|
||||
@@ -540,6 +561,14 @@ type ClientStreamMessage_AgentStateRes struct {
|
||||
AgentStateRes *AgentStateRes `protobuf:"bytes,5,opt,name=agentStateRes,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ClientStreamMessage_VTPMattestationReport struct {
|
||||
VTPMattestationReport *AttestationResponse `protobuf:"bytes,6,opt,name=vTPMattestationReport,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ClientStreamMessage_AzureAttestationResult struct {
|
||||
AzureAttestationResult *AzureAttestationResponse `protobuf:"bytes,7,opt,name=azureAttestationResult,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*ClientStreamMessage_AgentLog) isClientStreamMessage_Message() {}
|
||||
|
||||
func (*ClientStreamMessage_AgentEvent) isClientStreamMessage_Message() {}
|
||||
@@ -550,6 +579,10 @@ func (*ClientStreamMessage_StopComputationRes) isClientStreamMessage_Message() {
|
||||
|
||||
func (*ClientStreamMessage_AgentStateRes) isClientStreamMessage_Message() {}
|
||||
|
||||
func (*ClientStreamMessage_VTPMattestationReport) isClientStreamMessage_Message() {}
|
||||
|
||||
func (*ClientStreamMessage_AzureAttestationResult) isClientStreamMessage_Message() {}
|
||||
|
||||
type ServerStreamMessage struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Types that are valid to be assigned to Message:
|
||||
@@ -558,6 +591,7 @@ type ServerStreamMessage struct {
|
||||
// *ServerStreamMessage_RunReq
|
||||
// *ServerStreamMessage_StopComputation
|
||||
// *ServerStreamMessage_AgentStateReq
|
||||
// *ServerStreamMessage_DisconnectReq
|
||||
Message isServerStreamMessage_Message `protobuf_oneof:"message"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -636,6 +670,15 @@ func (x *ServerStreamMessage) GetAgentStateReq() *AgentStateReq {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ServerStreamMessage) GetDisconnectReq() *DisconnectReq {
|
||||
if x != nil {
|
||||
if x, ok := x.Message.(*ServerStreamMessage_DisconnectReq); ok {
|
||||
return x.DisconnectReq
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isServerStreamMessage_Message interface {
|
||||
isServerStreamMessage_Message()
|
||||
}
|
||||
@@ -656,6 +699,10 @@ type ServerStreamMessage_AgentStateReq struct {
|
||||
AgentStateReq *AgentStateReq `protobuf:"bytes,4,opt,name=agentStateReq,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ServerStreamMessage_DisconnectReq struct {
|
||||
DisconnectReq *DisconnectReq `protobuf:"bytes,5,opt,name=disconnectReq,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*ServerStreamMessage_RunReqChunks) isServerStreamMessage_Message() {}
|
||||
|
||||
func (*ServerStreamMessage_RunReq) isServerStreamMessage_Message() {}
|
||||
@@ -664,6 +711,52 @@ func (*ServerStreamMessage_StopComputation) isServerStreamMessage_Message() {}
|
||||
|
||||
func (*ServerStreamMessage_AgentStateReq) isServerStreamMessage_Message() {}
|
||||
|
||||
func (*ServerStreamMessage_DisconnectReq) isServerStreamMessage_Message() {}
|
||||
|
||||
type DisconnectReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *DisconnectReq) Reset() {
|
||||
*x = DisconnectReq{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *DisconnectReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DisconnectReq) ProtoMessage() {}
|
||||
|
||||
func (x *DisconnectReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DisconnectReq.ProtoReflect.Descriptor instead.
|
||||
func (*DisconnectReq) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *DisconnectReq) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RunReqChunks struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
|
||||
@@ -675,7 +768,7 @@ type RunReqChunks struct {
|
||||
|
||||
func (x *RunReqChunks) Reset() {
|
||||
*x = RunReqChunks{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[9]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -687,7 +780,7 @@ func (x *RunReqChunks) String() string {
|
||||
func (*RunReqChunks) ProtoMessage() {}
|
||||
|
||||
func (x *RunReqChunks) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[9]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -700,7 +793,7 @@ func (x *RunReqChunks) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RunReqChunks.ProtoReflect.Descriptor instead.
|
||||
func (*RunReqChunks) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{9}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *RunReqChunks) GetData() []byte {
|
||||
@@ -739,7 +832,7 @@ type ComputationRunReq struct {
|
||||
|
||||
func (x *ComputationRunReq) Reset() {
|
||||
*x = ComputationRunReq{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[10]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -751,7 +844,7 @@ func (x *ComputationRunReq) String() string {
|
||||
func (*ComputationRunReq) ProtoMessage() {}
|
||||
|
||||
func (x *ComputationRunReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[10]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -764,7 +857,7 @@ func (x *ComputationRunReq) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ComputationRunReq.ProtoReflect.Descriptor instead.
|
||||
func (*ComputationRunReq) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{10}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *ComputationRunReq) GetId() string {
|
||||
@@ -825,7 +918,7 @@ type ResultConsumer struct {
|
||||
|
||||
func (x *ResultConsumer) Reset() {
|
||||
*x = ResultConsumer{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[11]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -837,7 +930,7 @@ func (x *ResultConsumer) String() string {
|
||||
func (*ResultConsumer) ProtoMessage() {}
|
||||
|
||||
func (x *ResultConsumer) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[11]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -850,7 +943,7 @@ func (x *ResultConsumer) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ResultConsumer.ProtoReflect.Descriptor instead.
|
||||
func (*ResultConsumer) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{11}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *ResultConsumer) GetUserKey() []byte {
|
||||
@@ -871,7 +964,7 @@ type Dataset struct {
|
||||
|
||||
func (x *Dataset) Reset() {
|
||||
*x = Dataset{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[12]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -883,7 +976,7 @@ func (x *Dataset) String() string {
|
||||
func (*Dataset) ProtoMessage() {}
|
||||
|
||||
func (x *Dataset) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[12]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -896,7 +989,7 @@ func (x *Dataset) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Dataset.ProtoReflect.Descriptor instead.
|
||||
func (*Dataset) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{12}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *Dataset) GetHash() []byte {
|
||||
@@ -930,7 +1023,7 @@ type Algorithm struct {
|
||||
|
||||
func (x *Algorithm) Reset() {
|
||||
*x = Algorithm{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[13]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -942,7 +1035,7 @@ func (x *Algorithm) String() string {
|
||||
func (*Algorithm) ProtoMessage() {}
|
||||
|
||||
func (x *Algorithm) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[13]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -955,7 +1048,7 @@ func (x *Algorithm) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Algorithm.ProtoReflect.Descriptor instead.
|
||||
func (*Algorithm) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{13}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *Algorithm) GetHash() []byte {
|
||||
@@ -975,20 +1068,19 @@ func (x *Algorithm) GetUserKey() []byte {
|
||||
type AgentConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
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"`
|
||||
CertFile string `protobuf:"bytes,2,opt,name=cert_file,json=certFile,proto3" json:"cert_file,omitempty"`
|
||||
KeyFile string `protobuf:"bytes,3,opt,name=key_file,json=keyFile,proto3" json:"key_file,omitempty"`
|
||||
ClientCaFile string `protobuf:"bytes,4,opt,name=client_ca_file,json=clientCaFile,proto3" json:"client_ca_file,omitempty"`
|
||||
ServerCaFile string `protobuf:"bytes,5,opt,name=server_ca_file,json=serverCaFile,proto3" json:"server_ca_file,omitempty"`
|
||||
LogLevel string `protobuf:"bytes,6,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"`
|
||||
AttestedTls bool `protobuf:"varint,7,opt,name=attested_tls,json=attestedTls,proto3" json:"attested_tls,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AgentConfig) Reset() {
|
||||
*x = AgentConfig{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[14]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1000,7 +1092,7 @@ func (x *AgentConfig) String() string {
|
||||
func (*AgentConfig) ProtoMessage() {}
|
||||
|
||||
func (x *AgentConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[14]
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1013,7 +1105,7 @@ func (x *AgentConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AgentConfig.ProtoReflect.Descriptor instead.
|
||||
func (*AgentConfig) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{14}
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *AgentConfig) GetPort() string {
|
||||
@@ -1023,13 +1115,6 @@ func (x *AgentConfig) GetPort() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AgentConfig) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AgentConfig) GetCertFile() string {
|
||||
if x != nil {
|
||||
return x.CertFile
|
||||
@@ -1072,9 +1157,113 @@ func (x *AgentConfig) GetAttestedTls() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type AttestationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
|
||||
CertSerialNumber string `protobuf:"bytes,2,opt,name=certSerialNumber,proto3" json:"certSerialNumber,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AttestationResponse) Reset() {
|
||||
*x = AttestationResponse{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AttestationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AttestationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AttestationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AttestationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AttestationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *AttestationResponse) GetFile() []byte {
|
||||
if x != nil {
|
||||
return x.File
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AttestationResponse) GetCertSerialNumber() string {
|
||||
if x != nil {
|
||||
return x.CertSerialNumber
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AzureAttestationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
|
||||
CertSerialNumber string `protobuf:"bytes,2,opt,name=certSerialNumber,proto3" json:"certSerialNumber,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AzureAttestationResponse) Reset() {
|
||||
*x = AzureAttestationResponse{}
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AzureAttestationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AzureAttestationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AzureAttestationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_agent_cvms_cvms_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AzureAttestationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AzureAttestationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_agent_cvms_cvms_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *AzureAttestationResponse) GetFile() []byte {
|
||||
if x != nil {
|
||||
return x.File
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AzureAttestationResponse) GetCertSerialNumber() string {
|
||||
if x != nil {
|
||||
return x.CertSerialNumber
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_agent_cvms_cvms_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_agent_cvms_cvms_proto_rawDesc = []byte{
|
||||
var file_agent_cvms_cvms_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x15, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x76, 0x6d, 0x73, 0x2f, 0x63, 0x76, 0x6d,
|
||||
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x76, 0x6d, 0x73, 0x1a, 0x1f, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
|
||||
@@ -1122,7 +1311,7 @@ var file_agent_cvms_cvms_proto_rawDesc = []byte{
|
||||
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, 0xc0, 0x02, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xed, 0x03, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x2d, 0x0a, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4c,
|
||||
@@ -1141,138 +1330,171 @@ var file_agent_cvms_cvms_proto_rawDesc = []byte{
|
||||
0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||
0x52, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x76, 0x6d, 0x73,
|
||||
0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x48, 0x00,
|
||||
0x52, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x42,
|
||||
0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x13, 0x53,
|
||||
0x52, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x12,
|
||||
0x51, 0x0a, 0x15, 0x76, 0x54, 0x50, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
|
||||
0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x15, 0x76, 0x54, 0x50,
|
||||
0x4d, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x12, 0x58, 0x0a, 0x16, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x48, 0x00, 0x52, 0x16, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x09, 0x0a, 0x07,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x72, 0x76,
|
||||
0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x38, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x52, 0x75, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x75, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x72, 0x75, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x76, 0x6d, 0x73,
|
||||
0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x48, 0x00, 0x52, 0x06, 0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x41, 0x0a, 0x0f,
|
||||
0x73, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x53, 0x74, 0x6f,
|
||||
0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f,
|
||||
0x73, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x3b, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x41, 0x67,
|
||||
0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x48, 0x00, 0x52, 0x0d, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x3b, 0x0a, 0x0d,
|
||||
0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x48, 0x00, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x63,
|
||||
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x22, 0x1f, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
0x63, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x0c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x43,
|
||||
0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f,
|
||||
0x6c, 0x61, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4c, 0x61,
|
||||
0x73, 0x74, 0x22, 0xaa, 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, 0x29,
|
||||
0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x0d, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x52,
|
||||
0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x61, 0x6c, 0x67,
|
||||
0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63,
|
||||
0x76, 0x6d, 0x73, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x09, 0x61,
|
||||
0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x3f, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75,
|
||||
0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
|
||||
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
|
||||
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x0c, 0x61, 0x67, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x11, 0x2e, 0x63, 0x76, 0x6d, 0x73, 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,
|
||||
0x2a, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
|
||||
0x72, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x22, 0x53, 0x0a, 0x07, 0x44,
|
||||
0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73,
|
||||
0x65, 0x72, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x73, 0x65,
|
||||
0x72, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x22, 0x39, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73,
|
||||
0x68, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x22, 0xe5, 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,
|
||||
0x1b, 0x0a, 0x09, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 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, 0x03, 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, 0x04, 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,
|
||||
0x05, 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, 0x06, 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, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64,
|
||||
0x54, 0x6c, 0x73, 0x22, 0x55, 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, 0x12, 0x2a,
|
||||
0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x65, 0x72, 0x74, 0x53, 0x65,
|
||||
0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x5a, 0x0a, 0x18, 0x61, 0x7a,
|
||||
0x75, 0x72, 0x65, 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, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x65,
|
||||
0x72, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c,
|
||||
0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x32, 0x50, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x45, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x19, 0x2e, 0x63,
|
||||
0x76, 0x6d, 0x73, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x19, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x53,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e,
|
||||
0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e,
|
||||
0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x48, 0x00, 0x52, 0x0c,
|
||||
0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x31, 0x0a, 0x06,
|
||||
0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63,
|
||||
0x76, 0x6d, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x75, 0x6e, 0x52, 0x65, 0x71, 0x48, 0x00, 0x52, 0x06, 0x72, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x12,
|
||||
0x41, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48,
|
||||
0x00, 0x52, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||
0x52, 0x65, 0x71, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x76, 0x6d, 0x73,
|
||||
0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x48, 0x00,
|
||||
0x52, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x42,
|
||||
0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4b, 0x0a, 0x0c, 0x52, 0x75,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e,
|
||||
0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17,
|
||||
0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x06, 0x69, 0x73, 0x4c, 0x61, 0x73, 0x74, 0x22, 0xaa, 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, 0x29, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x18,
|
||||
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x44, 0x61, 0x74,
|
||||
0x61, 0x73, 0x65, 0x74, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2d,
|
||||
0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
|
||||
0x68, 0x6d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x3f, 0x0a,
|
||||
0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72,
|
||||
0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x52, 0x0f, 0x72,
|
||||
0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x12, 0x34,
|
||||
0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x76, 0x6d, 0x73, 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, 0x2a, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79,
|
||||
0x22, 0x53, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68,
|
||||
0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c,
|
||||
0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c,
|
||||
0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
|
||||
0x68, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65,
|
||||
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79,
|
||||
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, 0x50, 0x0a, 0x07,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65,
|
||||
0x73, 0x73, 0x12, 0x19, 0x2e, 0x63, 0x76, 0x6d, 0x73, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x19, 0x2e,
|
||||
0x63, 0x76, 0x6d, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08,
|
||||
0x5a, 0x06, 0x2e, 0x2f, 0x63, 0x76, 0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x63, 0x76,
|
||||
0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
file_agent_cvms_cvms_proto_rawDescOnce sync.Once
|
||||
file_agent_cvms_cvms_proto_rawDescData = file_agent_cvms_cvms_proto_rawDesc
|
||||
file_agent_cvms_cvms_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_agent_cvms_cvms_proto_rawDescGZIP() []byte {
|
||||
file_agent_cvms_cvms_proto_rawDescOnce.Do(func() {
|
||||
file_agent_cvms_cvms_proto_rawDescData = protoimpl.X.CompressGZIP(file_agent_cvms_cvms_proto_rawDescData)
|
||||
file_agent_cvms_cvms_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_agent_cvms_cvms_proto_rawDesc), len(file_agent_cvms_cvms_proto_rawDesc)))
|
||||
})
|
||||
return file_agent_cvms_cvms_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_agent_cvms_cvms_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_agent_cvms_cvms_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||
var file_agent_cvms_cvms_proto_goTypes = []any{
|
||||
(*AgentStateReq)(nil), // 0: cvms.AgentStateReq
|
||||
(*AgentStateRes)(nil), // 1: cvms.AgentStateRes
|
||||
(*StopComputation)(nil), // 2: cvms.StopComputation
|
||||
(*StopComputationResponse)(nil), // 3: cvms.StopComputationResponse
|
||||
(*RunResponse)(nil), // 4: cvms.RunResponse
|
||||
(*AgentEvent)(nil), // 5: cvms.AgentEvent
|
||||
(*AgentLog)(nil), // 6: cvms.AgentLog
|
||||
(*ClientStreamMessage)(nil), // 7: cvms.ClientStreamMessage
|
||||
(*ServerStreamMessage)(nil), // 8: cvms.ServerStreamMessage
|
||||
(*RunReqChunks)(nil), // 9: cvms.RunReqChunks
|
||||
(*ComputationRunReq)(nil), // 10: cvms.ComputationRunReq
|
||||
(*ResultConsumer)(nil), // 11: cvms.ResultConsumer
|
||||
(*Dataset)(nil), // 12: cvms.Dataset
|
||||
(*Algorithm)(nil), // 13: cvms.Algorithm
|
||||
(*AgentConfig)(nil), // 14: cvms.AgentConfig
|
||||
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
|
||||
(*AgentStateReq)(nil), // 0: cvms.AgentStateReq
|
||||
(*AgentStateRes)(nil), // 1: cvms.AgentStateRes
|
||||
(*StopComputation)(nil), // 2: cvms.StopComputation
|
||||
(*StopComputationResponse)(nil), // 3: cvms.StopComputationResponse
|
||||
(*RunResponse)(nil), // 4: cvms.RunResponse
|
||||
(*AgentEvent)(nil), // 5: cvms.AgentEvent
|
||||
(*AgentLog)(nil), // 6: cvms.AgentLog
|
||||
(*ClientStreamMessage)(nil), // 7: cvms.ClientStreamMessage
|
||||
(*ServerStreamMessage)(nil), // 8: cvms.ServerStreamMessage
|
||||
(*DisconnectReq)(nil), // 9: cvms.DisconnectReq
|
||||
(*RunReqChunks)(nil), // 10: cvms.RunReqChunks
|
||||
(*ComputationRunReq)(nil), // 11: cvms.ComputationRunReq
|
||||
(*ResultConsumer)(nil), // 12: cvms.ResultConsumer
|
||||
(*Dataset)(nil), // 13: cvms.Dataset
|
||||
(*Algorithm)(nil), // 14: cvms.Algorithm
|
||||
(*AgentConfig)(nil), // 15: cvms.AgentConfig
|
||||
(*AttestationResponse)(nil), // 16: cvms.AttestationResponse
|
||||
(*AzureAttestationResponse)(nil), // 17: cvms.azureAttestationResponse
|
||||
(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
|
||||
}
|
||||
var file_agent_cvms_cvms_proto_depIdxs = []int32{
|
||||
15, // 0: cvms.AgentEvent.timestamp:type_name -> google.protobuf.Timestamp
|
||||
15, // 1: cvms.AgentLog.timestamp:type_name -> google.protobuf.Timestamp
|
||||
18, // 0: cvms.AgentEvent.timestamp:type_name -> google.protobuf.Timestamp
|
||||
18, // 1: cvms.AgentLog.timestamp:type_name -> google.protobuf.Timestamp
|
||||
6, // 2: cvms.ClientStreamMessage.agent_log:type_name -> cvms.AgentLog
|
||||
5, // 3: cvms.ClientStreamMessage.agent_event:type_name -> cvms.AgentEvent
|
||||
4, // 4: cvms.ClientStreamMessage.run_res:type_name -> cvms.RunResponse
|
||||
3, // 5: cvms.ClientStreamMessage.stopComputationRes:type_name -> cvms.StopComputationResponse
|
||||
1, // 6: cvms.ClientStreamMessage.agentStateRes:type_name -> cvms.AgentStateRes
|
||||
9, // 7: cvms.ServerStreamMessage.runReqChunks:type_name -> cvms.RunReqChunks
|
||||
10, // 8: cvms.ServerStreamMessage.runReq:type_name -> cvms.ComputationRunReq
|
||||
2, // 9: cvms.ServerStreamMessage.stopComputation:type_name -> cvms.StopComputation
|
||||
0, // 10: cvms.ServerStreamMessage.agentStateReq:type_name -> cvms.AgentStateReq
|
||||
12, // 11: cvms.ComputationRunReq.datasets:type_name -> cvms.Dataset
|
||||
13, // 12: cvms.ComputationRunReq.algorithm:type_name -> cvms.Algorithm
|
||||
11, // 13: cvms.ComputationRunReq.result_consumers:type_name -> cvms.ResultConsumer
|
||||
14, // 14: cvms.ComputationRunReq.agent_config:type_name -> cvms.AgentConfig
|
||||
7, // 15: cvms.Service.Process:input_type -> cvms.ClientStreamMessage
|
||||
8, // 16: cvms.Service.Process:output_type -> cvms.ServerStreamMessage
|
||||
16, // [16:17] is the sub-list for method output_type
|
||||
15, // [15:16] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
16, // 7: cvms.ClientStreamMessage.vTPMattestationReport:type_name -> cvms.AttestationResponse
|
||||
17, // 8: cvms.ClientStreamMessage.azureAttestationResult:type_name -> cvms.azureAttestationResponse
|
||||
10, // 9: cvms.ServerStreamMessage.runReqChunks:type_name -> cvms.RunReqChunks
|
||||
11, // 10: cvms.ServerStreamMessage.runReq:type_name -> cvms.ComputationRunReq
|
||||
2, // 11: cvms.ServerStreamMessage.stopComputation:type_name -> cvms.StopComputation
|
||||
0, // 12: cvms.ServerStreamMessage.agentStateReq:type_name -> cvms.AgentStateReq
|
||||
9, // 13: cvms.ServerStreamMessage.disconnectReq:type_name -> cvms.DisconnectReq
|
||||
13, // 14: cvms.ComputationRunReq.datasets:type_name -> cvms.Dataset
|
||||
14, // 15: cvms.ComputationRunReq.algorithm:type_name -> cvms.Algorithm
|
||||
12, // 16: cvms.ComputationRunReq.result_consumers:type_name -> cvms.ResultConsumer
|
||||
15, // 17: cvms.ComputationRunReq.agent_config:type_name -> cvms.AgentConfig
|
||||
7, // 18: cvms.Service.Process:input_type -> cvms.ClientStreamMessage
|
||||
8, // 19: cvms.Service.Process:output_type -> cvms.ServerStreamMessage
|
||||
19, // [19:20] is the sub-list for method output_type
|
||||
18, // [18:19] is the sub-list for method input_type
|
||||
18, // [18:18] is the sub-list for extension type_name
|
||||
18, // [18:18] is the sub-list for extension extendee
|
||||
0, // [0:18] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_agent_cvms_cvms_proto_init() }
|
||||
@@ -1286,20 +1508,23 @@ func file_agent_cvms_cvms_proto_init() {
|
||||
(*ClientStreamMessage_RunRes)(nil),
|
||||
(*ClientStreamMessage_StopComputationRes)(nil),
|
||||
(*ClientStreamMessage_AgentStateRes)(nil),
|
||||
(*ClientStreamMessage_VTPMattestationReport)(nil),
|
||||
(*ClientStreamMessage_AzureAttestationResult)(nil),
|
||||
}
|
||||
file_agent_cvms_cvms_proto_msgTypes[8].OneofWrappers = []any{
|
||||
(*ServerStreamMessage_RunReqChunks)(nil),
|
||||
(*ServerStreamMessage_RunReq)(nil),
|
||||
(*ServerStreamMessage_StopComputation)(nil),
|
||||
(*ServerStreamMessage_AgentStateReq)(nil),
|
||||
(*ServerStreamMessage_DisconnectReq)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_agent_cvms_cvms_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_cvms_cvms_proto_rawDesc), len(file_agent_cvms_cvms_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 15,
|
||||
NumMessages: 18,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
@@ -1308,7 +1533,6 @@ func file_agent_cvms_cvms_proto_init() {
|
||||
MessageInfos: file_agent_cvms_cvms_proto_msgTypes,
|
||||
}.Build()
|
||||
File_agent_cvms_cvms_proto = out.File
|
||||
file_agent_cvms_cvms_proto_rawDesc = nil
|
||||
file_agent_cvms_cvms_proto_goTypes = nil
|
||||
file_agent_cvms_cvms_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
+23
-7
@@ -59,6 +59,8 @@ message ClientStreamMessage {
|
||||
RunResponse run_res = 3;
|
||||
StopComputationResponse stopComputationRes = 4;
|
||||
AgentStateRes agentStateRes = 5;
|
||||
AttestationResponse vTPMattestationReport = 6;
|
||||
azureAttestationResponse azureAttestationResult = 7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +70,14 @@ message ServerStreamMessage {
|
||||
ComputationRunReq runReq = 2;
|
||||
StopComputation stopComputation = 3;
|
||||
AgentStateReq agentStateReq = 4;
|
||||
DisconnectReq disconnectReq = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message DisconnectReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message RunReqChunks {
|
||||
bytes data = 1;
|
||||
string id = 2;
|
||||
@@ -104,11 +111,20 @@ message Algorithm {
|
||||
|
||||
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;
|
||||
string cert_file = 2;
|
||||
string key_file = 3;
|
||||
string client_ca_file = 4;
|
||||
string server_ca_file = 5;
|
||||
string log_level = 6;
|
||||
bool attested_tls = 7;
|
||||
}
|
||||
|
||||
message AttestationResponse {
|
||||
bytes file = 1;
|
||||
string certSerialNumber = 2;
|
||||
}
|
||||
|
||||
message azureAttestationResponse {
|
||||
bytes file = 1;
|
||||
string certSerialNumber = 2;
|
||||
}
|
||||
|
||||
+12
-10
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/ultravioletrs/cocos/agent/auth"
|
||||
"github.com/ultravioletrs/cocos/internal/server"
|
||||
grpcserver "github.com/ultravioletrs/cocos/internal/server/grpc"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
@@ -32,12 +31,18 @@ type agentServer struct {
|
||||
gs server.Server
|
||||
logger *slog.Logger
|
||||
svc agent.Service
|
||||
host string
|
||||
caUrl string
|
||||
cvmId string
|
||||
}
|
||||
|
||||
func NewServer(logger *slog.Logger, svc agent.Service) AgentServer {
|
||||
func NewServer(logger *slog.Logger, svc agent.Service, host string, caUrl string, cvmId string) AgentServer {
|
||||
return &agentServer{
|
||||
logger: logger,
|
||||
svc: svc,
|
||||
host: host,
|
||||
caUrl: caUrl,
|
||||
cvmId: cvmId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +54,7 @@ func (as *agentServer) Start(cfg agent.AgentConfig, cmp agent.Computation) error
|
||||
agentGrpcServerConfig := server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: cfg.Host,
|
||||
Host: as.host,
|
||||
Port: cfg.Port,
|
||||
CertFile: cfg.CertFile,
|
||||
KeyFile: cfg.KeyFile,
|
||||
@@ -71,15 +76,9 @@ func (as *agentServer) Start(cfg agent.AgentConfig, cmp agent.Computation) error
|
||||
return err
|
||||
}
|
||||
|
||||
qp, err := quoteprovider.GetLeveledQuoteProvider()
|
||||
if err != nil {
|
||||
as.logger.Error(fmt.Sprintf("failed to create quote provider %s", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
as.gs = grpcserver.New(ctx, cancel, svcName, agentGrpcServerConfig, registerAgentServiceServer, as.logger, qp, authSvc)
|
||||
as.gs = grpcserver.New(ctx, cancel, svcName, agentGrpcServerConfig, registerAgentServiceServer, as.logger, authSvc, as.caUrl, as.cvmId)
|
||||
|
||||
go func() {
|
||||
err := as.gs.Start()
|
||||
@@ -92,5 +91,8 @@ func (as *agentServer) Start(cfg agent.AgentConfig, cmp agent.Computation) error
|
||||
}
|
||||
|
||||
func (as *agentServer) Stop() error {
|
||||
if as.gs == nil {
|
||||
return nil
|
||||
}
|
||||
return as.gs.Stop()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,544 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"github.com/ultravioletrs/cocos/agent/mocks"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*slog.Logger, *mocks.Service, string, string, string, []byte) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
mockSvc := new(mocks.Service)
|
||||
host := "localhost"
|
||||
caUrl := "https://ca.example.com"
|
||||
cvmId := "test-cvm-id"
|
||||
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.NoError(t, err, "Failed to generate ECDSA key")
|
||||
|
||||
pubkey, err := x509.MarshalPKIXPublicKey(privateKey.Public())
|
||||
assert.NoError(t, err, "Failed to marshal public key")
|
||||
|
||||
return logger, mockSvc, host, caUrl, cvmId, pubkey
|
||||
}
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, _ := setupTest(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
logger *slog.Logger
|
||||
svc agent.Service
|
||||
host string
|
||||
caUrl string
|
||||
cvmId string
|
||||
expected AgentServer
|
||||
}{
|
||||
{
|
||||
name: "valid server creation",
|
||||
logger: logger,
|
||||
svc: svc,
|
||||
host: host,
|
||||
caUrl: caUrl,
|
||||
cvmId: cvmId,
|
||||
},
|
||||
{
|
||||
name: "server with empty host",
|
||||
logger: logger,
|
||||
svc: svc,
|
||||
host: "",
|
||||
caUrl: caUrl,
|
||||
cvmId: cvmId,
|
||||
},
|
||||
{
|
||||
name: "server with empty caUrl",
|
||||
logger: logger,
|
||||
svc: svc,
|
||||
host: host,
|
||||
caUrl: "",
|
||||
cvmId: cvmId,
|
||||
},
|
||||
{
|
||||
name: "server with empty cvmId",
|
||||
logger: logger,
|
||||
svc: svc,
|
||||
host: host,
|
||||
caUrl: caUrl,
|
||||
cvmId: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := NewServer(tt.logger, tt.svc, tt.host, tt.caUrl, tt.cvmId)
|
||||
|
||||
assert.NotNil(t, server)
|
||||
|
||||
agentSrv, ok := server.(*agentServer)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, tt.logger, agentSrv.logger)
|
||||
assert.Equal(t, tt.svc, agentSrv.svc)
|
||||
assert.Equal(t, tt.host, agentSrv.host)
|
||||
assert.Equal(t, tt.caUrl, agentSrv.caUrl)
|
||||
assert.Equal(t, tt.cvmId, agentSrv.cvmId)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentServer_Start(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, pubKey := setupTest(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg agent.AgentConfig
|
||||
cmp agent.Computation
|
||||
setupMocks func(*mocks.Service)
|
||||
expectedError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "successful start with default port",
|
||||
cfg: agent.AgentConfig{
|
||||
Port: "",
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
ServerCAFile: "server-ca.pem",
|
||||
ClientCAFile: "client-ca.pem",
|
||||
AttestedTls: true,
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "test-computation-1",
|
||||
Name: "Test Computation",
|
||||
Description: "A test computation",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x01, 0x02, 0x03},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x04, 0x05, 0x06},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
setupMocks: func(m *mocks.Service) {
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "successful start with custom port",
|
||||
cfg: agent.AgentConfig{
|
||||
Port: "8080",
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
ServerCAFile: "server-ca.pem",
|
||||
ClientCAFile: "client-ca.pem",
|
||||
AttestedTls: false,
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "test-computation-2",
|
||||
Name: "Test Computation 2",
|
||||
Description: "Another test computation",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x07, 0x08, 0x09},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x0a, 0x0b, 0x0c},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
setupMocks: func(m *mocks.Service) {
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "start with minimal config",
|
||||
cfg: agent.AgentConfig{
|
||||
Port: "9090",
|
||||
AttestedTls: false,
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "test-computation-3",
|
||||
Name: "Minimal Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x0d, 0x0e, 0x0f},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x10, 0x11, 0x12},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
setupMocks: func(m *mocks.Service) {
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.setupMocks(svc)
|
||||
|
||||
server := NewServer(logger, svc, host, caUrl, cvmId)
|
||||
|
||||
err := server.Start(tt.cfg, tt.cmp)
|
||||
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err)
|
||||
if tt.errorContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the port was set correctly
|
||||
agentSrv := server.(*agentServer)
|
||||
assert.NotNil(t, agentSrv.gs)
|
||||
|
||||
if err := server.Stop(); err != nil {
|
||||
t.Fatalf("Failed to stop server after start: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentServer_Stop(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, pubKey := setupTest(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setupServer func(AgentServer) error
|
||||
expectedError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "stop unstarted server",
|
||||
setupServer: func(server AgentServer) error {
|
||||
// Don't start the server
|
||||
return nil
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "stop started server",
|
||||
setupServer: func(server AgentServer) error {
|
||||
cfg := agent.AgentConfig{
|
||||
Port: "7004",
|
||||
}
|
||||
cmp := agent.Computation{
|
||||
ID: "test-stop-computation",
|
||||
Name: "Stop Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x19, 0x1a, 0x1b},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x1c, 0x1d, 0x1e},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
return server.Start(cfg, cmp)
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := NewServer(logger, svc, host, caUrl, cvmId)
|
||||
|
||||
err := tt.setupServer(server)
|
||||
if err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
// Give the server a moment to start if it was started
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
err = server.Stop()
|
||||
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err)
|
||||
if tt.errorContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentServer_StopMultipleTimes(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, pubKey := setupTest(t)
|
||||
server := NewServer(logger, svc, host, caUrl, cvmId)
|
||||
|
||||
// Start the server
|
||||
cfg := agent.AgentConfig{Port: "7005"}
|
||||
cmp := agent.Computation{
|
||||
ID: "test-multiple-stop",
|
||||
Name: "Multiple Stop Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x1f, 0x20, 0x21},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x22, 0x23, 0x24},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := server.Start(cfg, cmp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Give the server a moment to start
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Stop the server multiple times
|
||||
err1 := server.Stop()
|
||||
err2 := server.Stop()
|
||||
err3 := server.Stop()
|
||||
|
||||
assert.NoError(t, err1)
|
||||
assert.NoError(t, err2)
|
||||
assert.NoError(t, err3)
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestAgentServer_StartAfterStop(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, pubKey := setupTest(t)
|
||||
server := NewServer(logger, svc, host, caUrl, cvmId)
|
||||
|
||||
cfg := agent.AgentConfig{Port: "7006"}
|
||||
cmp := agent.Computation{
|
||||
ID: "test-restart",
|
||||
Name: "Restart Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x25, 0x26, 0x27},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x28, 0x29, 0x2a},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Start, stop, then start again
|
||||
err := server.Start(cfg, cmp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
err = server.Stop()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Start again with different config
|
||||
cfg2 := agent.AgentConfig{Port: "7007"}
|
||||
cmp2 := agent.Computation{
|
||||
ID: "test-restart-2",
|
||||
Name: "Restart Test 2",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x2b, 0x2c, 0x2d},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x2e, 0x2f, 0x30},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = server.Start(cfg2, cmp2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
err = server.Stop()
|
||||
assert.NoError(t, err)
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestAgentServer_ConfigValidation(t *testing.T) {
|
||||
logger, svc, host, caUrl, cvmId, pubKey := setupTest(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config agent.AgentConfig
|
||||
cmp agent.Computation
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "valid config with all fields",
|
||||
config: agent.AgentConfig{
|
||||
Port: "8080",
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
ServerCAFile: "server-ca.pem",
|
||||
ClientCAFile: "client-ca.pem",
|
||||
AttestedTls: true,
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "valid-config-test",
|
||||
Name: "Valid Config Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x31, 0x32, 0x33},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x34, 0x35, 0x36},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "valid config with minimal fields",
|
||||
config: agent.AgentConfig{
|
||||
Port: "9090",
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "minimal-config-test",
|
||||
Name: "Minimal Config Test",
|
||||
Algorithm: agent.Algorithm{
|
||||
Hash: [32]byte{0x37, 0x38, 0x39},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
Datasets: []agent.Dataset{
|
||||
{
|
||||
Hash: [32]byte{0x3a, 0x3b, 0x3c},
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{
|
||||
UserKey: pubKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "config with empty port uses default",
|
||||
config: agent.AgentConfig{
|
||||
Port: "",
|
||||
},
|
||||
cmp: agent.Computation{
|
||||
ID: "default-port-test",
|
||||
Name: "Default Port Test",
|
||||
Algorithm: agent.Algorithm{Hash: [32]byte{0x3d, 0x3e, 0x3f}, UserKey: pubKey},
|
||||
Datasets: []agent.Dataset{
|
||||
{Hash: [32]byte{0x40, 0x41, 0x42}, UserKey: pubKey},
|
||||
},
|
||||
ResultConsumers: []agent.ResultConsumer{
|
||||
{UserKey: pubKey},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := NewServer(logger, svc, host, caUrl, cvmId)
|
||||
|
||||
err := server.Start(tt.config, tt.cmp)
|
||||
|
||||
if tt.valid {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify default port is used when empty
|
||||
if tt.config.Port == "" {
|
||||
agentSrv := server.(*agentServer)
|
||||
assert.NotNil(t, agentSrv.gs)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if err := server.Stop(); err != nil {
|
||||
t.Fatalf("Failed to stop server after start: %v", err)
|
||||
}
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
svc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
assert.Equal(t, "agent", svcName)
|
||||
assert.Equal(t, "7002", defSvcGRPCPort)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -70,7 +70,7 @@ func (_c *AgentServer_Start_Call) RunAndReturn(run func(agent.AgentConfig, agent
|
||||
return _c
|
||||
}
|
||||
|
||||
// Stop provides a mock function with given fields:
|
||||
// Stop provides a mock function with no fields
|
||||
func (_m *AgentServer) Stop() error {
|
||||
ret := _m.Called()
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.0
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc v5.29.0
|
||||
// source: agent/events/events.proto
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -260,7 +261,7 @@ func (*EventsLogs_AgentEvent) isEventsLogs_Message() {}
|
||||
|
||||
var File_agent_events_events_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_agent_events_events_proto_rawDesc = []byte{
|
||||
var file_agent_events_events_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x19, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x65,
|
||||
0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x65, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
@@ -299,16 +300,16 @@ var file_agent_events_events_proto_rawDesc = []byte{
|
||||
0x67, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
})
|
||||
|
||||
var (
|
||||
file_agent_events_events_proto_rawDescOnce sync.Once
|
||||
file_agent_events_events_proto_rawDescData = file_agent_events_events_proto_rawDesc
|
||||
file_agent_events_events_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_agent_events_events_proto_rawDescGZIP() []byte {
|
||||
file_agent_events_events_proto_rawDescOnce.Do(func() {
|
||||
file_agent_events_events_proto_rawDescData = protoimpl.X.CompressGZIP(file_agent_events_events_proto_rawDescData)
|
||||
file_agent_events_events_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_agent_events_events_proto_rawDesc), len(file_agent_events_events_proto_rawDesc)))
|
||||
})
|
||||
return file_agent_events_events_proto_rawDescData
|
||||
}
|
||||
@@ -345,7 +346,7 @@ func file_agent_events_events_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_agent_events_events_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_events_events_proto_rawDesc), len(file_agent_events_events_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
@@ -356,7 +357,6 @@ func file_agent_events_events_proto_init() {
|
||||
MessageInfos: file_agent_events_events_proto_msgTypes,
|
||||
}.Build()
|
||||
File_agent_events_events_proto = out.File
|
||||
file_agent_events_events_proto_rawDesc = nil
|
||||
file_agent_events_events_proto_goTypes = nil
|
||||
file_agent_events_events_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -56,7 +56,7 @@ func (_c *Service_SendEvent_Call) Return() *Service_SendEvent_Call {
|
||||
}
|
||||
|
||||
func (_c *Service_SendEvent_Call) RunAndReturn(run func(string, string, string, json.RawMessage)) *Service_SendEvent_Call {
|
||||
_c.Call.Return(run)
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
|
||||
+138
-11
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
agent "github.com/ultravioletrs/cocos/agent"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
attestation "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
|
||||
context "context"
|
||||
|
||||
@@ -75,7 +75,7 @@ func (_c *Service_Algo_Call) RunAndReturn(run func(context.Context, agent.Algori
|
||||
}
|
||||
|
||||
// Attestation provides a mock function with given fields: ctx, reportData, nonce, attType
|
||||
func (_m *Service) Attestation(ctx context.Context, reportData [64]byte, nonce [32]byte, attType config.AttestationType) ([]byte, error) {
|
||||
func (_m *Service) Attestation(ctx context.Context, reportData [64]byte, nonce [32]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
ret := _m.Called(ctx, reportData, nonce, attType)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -84,10 +84,10 @@ func (_m *Service) Attestation(ctx context.Context, reportData [64]byte, nonce [
|
||||
|
||||
var r0 []byte
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [64]byte, [32]byte, config.AttestationType) ([]byte, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [64]byte, [32]byte, attestation.PlatformType) ([]byte, error)); ok {
|
||||
return rf(ctx, reportData, nonce, attType)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [64]byte, [32]byte, config.AttestationType) []byte); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [64]byte, [32]byte, attestation.PlatformType) []byte); ok {
|
||||
r0 = rf(ctx, reportData, nonce, attType)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
@@ -95,7 +95,7 @@ func (_m *Service) Attestation(ctx context.Context, reportData [64]byte, nonce [
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, [64]byte, [32]byte, config.AttestationType) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, [64]byte, [32]byte, attestation.PlatformType) error); ok {
|
||||
r1 = rf(ctx, reportData, nonce, attType)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -113,14 +113,14 @@ type Service_Attestation_Call struct {
|
||||
// - ctx context.Context
|
||||
// - reportData [64]byte
|
||||
// - nonce [32]byte
|
||||
// - attType config.AttestationType
|
||||
// - attType attestation.PlatformType
|
||||
func (_e *Service_Expecter) Attestation(ctx interface{}, reportData interface{}, nonce interface{}, attType interface{}) *Service_Attestation_Call {
|
||||
return &Service_Attestation_Call{Call: _e.mock.On("Attestation", ctx, reportData, nonce, attType)}
|
||||
}
|
||||
|
||||
func (_c *Service_Attestation_Call) Run(run func(ctx context.Context, reportData [64]byte, nonce [32]byte, attType config.AttestationType)) *Service_Attestation_Call {
|
||||
func (_c *Service_Attestation_Call) Run(run func(ctx context.Context, reportData [64]byte, nonce [32]byte, attType attestation.PlatformType)) *Service_Attestation_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].([64]byte), args[2].([32]byte), args[3].(config.AttestationType))
|
||||
run(args[0].(context.Context), args[1].([64]byte), args[2].([32]byte), args[3].(attestation.PlatformType))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
@@ -130,7 +130,67 @@ func (_c *Service_Attestation_Call) Return(_a0 []byte, _a1 error) *Service_Attes
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_Attestation_Call) RunAndReturn(run func(context.Context, [64]byte, [32]byte, config.AttestationType) ([]byte, error)) *Service_Attestation_Call {
|
||||
func (_c *Service_Attestation_Call) RunAndReturn(run func(context.Context, [64]byte, [32]byte, attestation.PlatformType) ([]byte, error)) *Service_Attestation_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AttestationResult provides a mock function with given fields: ctx, nonce, attType
|
||||
func (_m *Service) AttestationResult(ctx context.Context, nonce [32]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
ret := _m.Called(ctx, nonce, attType)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AttestationResult")
|
||||
}
|
||||
|
||||
var r0 []byte
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [32]byte, attestation.PlatformType) ([]byte, error)); ok {
|
||||
return rf(ctx, nonce, attType)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [32]byte, attestation.PlatformType) []byte); ok {
|
||||
r0 = rf(ctx, nonce, attType)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]byte)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, [32]byte, attestation.PlatformType) error); ok {
|
||||
r1 = rf(ctx, nonce, attType)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_AttestationResult_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AttestationResult'
|
||||
type Service_AttestationResult_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// AttestationResult is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - nonce [32]byte
|
||||
// - attType attestation.PlatformType
|
||||
func (_e *Service_Expecter) AttestationResult(ctx interface{}, nonce interface{}, attType interface{}) *Service_AttestationResult_Call {
|
||||
return &Service_AttestationResult_Call{Call: _e.mock.On("AttestationResult", ctx, nonce, attType)}
|
||||
}
|
||||
|
||||
func (_c *Service_AttestationResult_Call) Run(run func(ctx context.Context, nonce [32]byte, attType attestation.PlatformType)) *Service_AttestationResult_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].([32]byte), args[2].(attestation.PlatformType))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_AttestationResult_Call) Return(_a0 []byte, _a1 error) *Service_AttestationResult_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_AttestationResult_Call) RunAndReturn(run func(context.Context, [32]byte, attestation.PlatformType) ([]byte, error)) *Service_AttestationResult_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -182,6 +242,73 @@ func (_c *Service_Data_Call) RunAndReturn(run func(context.Context, agent.Datase
|
||||
return _c
|
||||
}
|
||||
|
||||
// IMAMeasurements provides a mock function with given fields: ctx
|
||||
func (_m *Service) IMAMeasurements(ctx context.Context) ([]byte, []byte, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IMAMeasurements")
|
||||
}
|
||||
|
||||
var r0 []byte
|
||||
var r1 []byte
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) ([]byte, []byte, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) []byte); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]byte)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) []byte); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).([]byte)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
|
||||
r2 = rf(ctx)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// Service_IMAMeasurements_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IMAMeasurements'
|
||||
type Service_IMAMeasurements_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// IMAMeasurements is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
func (_e *Service_Expecter) IMAMeasurements(ctx interface{}) *Service_IMAMeasurements_Call {
|
||||
return &Service_IMAMeasurements_Call{Call: _e.mock.On("IMAMeasurements", ctx)}
|
||||
}
|
||||
|
||||
func (_c *Service_IMAMeasurements_Call) Run(run func(ctx context.Context)) *Service_IMAMeasurements_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_IMAMeasurements_Call) Return(_a0 []byte, _a1 []byte, _a2 error) *Service_IMAMeasurements_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_IMAMeasurements_Call) RunAndReturn(run func(context.Context) ([]byte, []byte, error)) *Service_IMAMeasurements_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// InitComputation provides a mock function with given fields: ctx, cmp
|
||||
func (_m *Service) InitComputation(ctx context.Context, cmp agent.Computation) error {
|
||||
ret := _m.Called(ctx, cmp)
|
||||
@@ -287,7 +414,7 @@ func (_c *Service_Result_Call) RunAndReturn(run func(context.Context) ([]byte, e
|
||||
return _c
|
||||
}
|
||||
|
||||
// State provides a mock function with given fields:
|
||||
// State provides a mock function with no fields
|
||||
func (_m *Service) State() string {
|
||||
ret := _m.Called()
|
||||
|
||||
|
||||
+246
-19
@@ -1,8 +1,8 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
@@ -16,12 +16,20 @@ import (
|
||||
)
|
||||
|
||||
// AgentService_AlgoClient is an autogenerated mock type for the AgentService_AlgoClient type
|
||||
type AgentService_AlgoClient struct {
|
||||
type AgentService_AlgoClient[Req interface{}, Res interface{}] struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CloseAndRecv provides a mock function with given fields:
|
||||
func (_m *AgentService_AlgoClient) CloseAndRecv() (*agent.AlgoResponse, error) {
|
||||
type AgentService_AlgoClient_Expecter[Req interface{}, Res interface{}] struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) EXPECT() *AgentService_AlgoClient_Expecter[Req, Res] {
|
||||
return &AgentService_AlgoClient_Expecter[Req, Res]{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// CloseAndRecv provides a mock function with no fields
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) CloseAndRecv() (*agent.AlgoResponse, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -50,8 +58,35 @@ func (_m *AgentService_AlgoClient) CloseAndRecv() (*agent.AlgoResponse, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with given fields:
|
||||
func (_m *AgentService_AlgoClient) CloseSend() error {
|
||||
// AgentService_AlgoClient_CloseAndRecv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseAndRecv'
|
||||
type AgentService_AlgoClient_CloseAndRecv_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CloseAndRecv is a helper method to define mock.On call
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) CloseAndRecv() *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_CloseAndRecv_Call[Req, Res]{Call: _e.mock.On("CloseAndRecv")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res]) Run(run func()) *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res]) Return(_a0 *agent.AlgoResponse, _a1 error) *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res]) RunAndReturn(run func() (*agent.AlgoResponse, error)) *AgentService_AlgoClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with no fields
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) CloseSend() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -68,8 +103,35 @@ func (_m *AgentService_AlgoClient) CloseSend() error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Context provides a mock function with given fields:
|
||||
func (_m *AgentService_AlgoClient) Context() context.Context {
|
||||
// AgentService_AlgoClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend'
|
||||
type AgentService_AlgoClient_CloseSend_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CloseSend is a helper method to define mock.On call
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) CloseSend() *AgentService_AlgoClient_CloseSend_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_CloseSend_Call[Req, Res]{Call: _e.mock.On("CloseSend")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseSend_Call[Req, Res]) Run(run func()) *AgentService_AlgoClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseSend_Call[Req, Res]) Return(_a0 error) *AgentService_AlgoClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_CloseSend_Call[Req, Res]) RunAndReturn(run func() error) *AgentService_AlgoClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Context provides a mock function with no fields
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) Context() context.Context {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -88,8 +150,35 @@ func (_m *AgentService_AlgoClient) Context() context.Context {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Header provides a mock function with given fields:
|
||||
func (_m *AgentService_AlgoClient) Header() (metadata.MD, error) {
|
||||
// AgentService_AlgoClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context'
|
||||
type AgentService_AlgoClient_Context_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Context is a helper method to define mock.On call
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) Context() *AgentService_AlgoClient_Context_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_Context_Call[Req, Res]{Call: _e.mock.On("Context")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Context_Call[Req, Res]) Run(run func()) *AgentService_AlgoClient_Context_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Context_Call[Req, Res]) Return(_a0 context.Context) *AgentService_AlgoClient_Context_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Context_Call[Req, Res]) RunAndReturn(run func() context.Context) *AgentService_AlgoClient_Context_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Header provides a mock function with no fields
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) Header() (metadata.MD, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -118,8 +207,35 @@ func (_m *AgentService_AlgoClient) Header() (metadata.MD, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AgentService_AlgoClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header'
|
||||
type AgentService_AlgoClient_Header_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Header is a helper method to define mock.On call
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) Header() *AgentService_AlgoClient_Header_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_Header_Call[Req, Res]{Call: _e.mock.On("Header")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Header_Call[Req, Res]) Run(run func()) *AgentService_AlgoClient_Header_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Header_Call[Req, Res]) Return(_a0 metadata.MD, _a1 error) *AgentService_AlgoClient_Header_Call[Req, Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Header_Call[Req, Res]) RunAndReturn(run func() (metadata.MD, error)) *AgentService_AlgoClient_Header_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RecvMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_AlgoClient) RecvMsg(m interface{}) error {
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) RecvMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -136,8 +252,36 @@ func (_m *AgentService_AlgoClient) RecvMsg(m interface{}) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_AlgoClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg'
|
||||
type AgentService_AlgoClient_RecvMsg_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RecvMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) RecvMsg(m interface{}) *AgentService_AlgoClient_RecvMsg_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_RecvMsg_Call[Req, Res]{Call: _e.mock.On("RecvMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_RecvMsg_Call[Req, Res]) Run(run func(m interface{})) *AgentService_AlgoClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_RecvMsg_Call[Req, Res]) Return(_a0 error) *AgentService_AlgoClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_RecvMsg_Call[Req, Res]) RunAndReturn(run func(interface{}) error) *AgentService_AlgoClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Send provides a mock function with given fields: _a0
|
||||
func (_m *AgentService_AlgoClient) Send(_a0 *agent.AlgoRequest) error {
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) Send(_a0 *agent.AlgoRequest) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -154,8 +298,36 @@ func (_m *AgentService_AlgoClient) Send(_a0 *agent.AlgoRequest) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_AlgoClient_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send'
|
||||
type AgentService_AlgoClient_Send_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Send is a helper method to define mock.On call
|
||||
// - _a0 *agent.AlgoRequest
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) Send(_a0 interface{}) *AgentService_AlgoClient_Send_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_Send_Call[Req, Res]{Call: _e.mock.On("Send", _a0)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Send_Call[Req, Res]) Run(run func(_a0 *agent.AlgoRequest)) *AgentService_AlgoClient_Send_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(*agent.AlgoRequest))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Send_Call[Req, Res]) Return(_a0 error) *AgentService_AlgoClient_Send_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Send_Call[Req, Res]) RunAndReturn(run func(*agent.AlgoRequest) error) *AgentService_AlgoClient_Send_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_AlgoClient) SendMsg(m interface{}) error {
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) SendMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -172,8 +344,36 @@ func (_m *AgentService_AlgoClient) SendMsg(m interface{}) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with given fields:
|
||||
func (_m *AgentService_AlgoClient) Trailer() metadata.MD {
|
||||
// AgentService_AlgoClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg'
|
||||
type AgentService_AlgoClient_SendMsg_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) SendMsg(m interface{}) *AgentService_AlgoClient_SendMsg_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_SendMsg_Call[Req, Res]{Call: _e.mock.On("SendMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_SendMsg_Call[Req, Res]) Run(run func(m interface{})) *AgentService_AlgoClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_SendMsg_Call[Req, Res]) Return(_a0 error) *AgentService_AlgoClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_SendMsg_Call[Req, Res]) RunAndReturn(run func(interface{}) error) *AgentService_AlgoClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with no fields
|
||||
func (_m *AgentService_AlgoClient[Req, Res]) Trailer() metadata.MD {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -192,13 +392,40 @@ func (_m *AgentService_AlgoClient) Trailer() metadata.MD {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_AlgoClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer'
|
||||
type AgentService_AlgoClient_Trailer_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Trailer is a helper method to define mock.On call
|
||||
func (_e *AgentService_AlgoClient_Expecter[Req, Res]) Trailer() *AgentService_AlgoClient_Trailer_Call[Req, Res] {
|
||||
return &AgentService_AlgoClient_Trailer_Call[Req, Res]{Call: _e.mock.On("Trailer")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Trailer_Call[Req, Res]) Run(run func()) *AgentService_AlgoClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Trailer_Call[Req, Res]) Return(_a0 metadata.MD) *AgentService_AlgoClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_AlgoClient_Trailer_Call[Req, Res]) RunAndReturn(run func() metadata.MD) *AgentService_AlgoClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewAgentService_AlgoClient creates a new instance of AgentService_AlgoClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAgentService_AlgoClient(t interface {
|
||||
func NewAgentService_AlgoClient[Req interface{}, Res interface{}](t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AgentService_AlgoClient {
|
||||
mock := &AgentService_AlgoClient{}
|
||||
}) *AgentService_AlgoClient[Req, Res] {
|
||||
mock := &AgentService_AlgoClient[Req, Res]{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
+246
-19
@@ -1,8 +1,8 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
@@ -16,12 +16,20 @@ import (
|
||||
)
|
||||
|
||||
// AgentService_DataClient is an autogenerated mock type for the AgentService_DataClient type
|
||||
type AgentService_DataClient struct {
|
||||
type AgentService_DataClient[Req interface{}, Res interface{}] struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CloseAndRecv provides a mock function with given fields:
|
||||
func (_m *AgentService_DataClient) CloseAndRecv() (*agent.DataResponse, error) {
|
||||
type AgentService_DataClient_Expecter[Req interface{}, Res interface{}] struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AgentService_DataClient[Req, Res]) EXPECT() *AgentService_DataClient_Expecter[Req, Res] {
|
||||
return &AgentService_DataClient_Expecter[Req, Res]{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// CloseAndRecv provides a mock function with no fields
|
||||
func (_m *AgentService_DataClient[Req, Res]) CloseAndRecv() (*agent.DataResponse, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -50,8 +58,35 @@ func (_m *AgentService_DataClient) CloseAndRecv() (*agent.DataResponse, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with given fields:
|
||||
func (_m *AgentService_DataClient) CloseSend() error {
|
||||
// AgentService_DataClient_CloseAndRecv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseAndRecv'
|
||||
type AgentService_DataClient_CloseAndRecv_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CloseAndRecv is a helper method to define mock.On call
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) CloseAndRecv() *AgentService_DataClient_CloseAndRecv_Call[Req, Res] {
|
||||
return &AgentService_DataClient_CloseAndRecv_Call[Req, Res]{Call: _e.mock.On("CloseAndRecv")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseAndRecv_Call[Req, Res]) Run(run func()) *AgentService_DataClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseAndRecv_Call[Req, Res]) Return(_a0 *agent.DataResponse, _a1 error) *AgentService_DataClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseAndRecv_Call[Req, Res]) RunAndReturn(run func() (*agent.DataResponse, error)) *AgentService_DataClient_CloseAndRecv_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with no fields
|
||||
func (_m *AgentService_DataClient[Req, Res]) CloseSend() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -68,8 +103,35 @@ func (_m *AgentService_DataClient) CloseSend() error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Context provides a mock function with given fields:
|
||||
func (_m *AgentService_DataClient) Context() context.Context {
|
||||
// AgentService_DataClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend'
|
||||
type AgentService_DataClient_CloseSend_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CloseSend is a helper method to define mock.On call
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) CloseSend() *AgentService_DataClient_CloseSend_Call[Req, Res] {
|
||||
return &AgentService_DataClient_CloseSend_Call[Req, Res]{Call: _e.mock.On("CloseSend")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseSend_Call[Req, Res]) Run(run func()) *AgentService_DataClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseSend_Call[Req, Res]) Return(_a0 error) *AgentService_DataClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_CloseSend_Call[Req, Res]) RunAndReturn(run func() error) *AgentService_DataClient_CloseSend_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Context provides a mock function with no fields
|
||||
func (_m *AgentService_DataClient[Req, Res]) Context() context.Context {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -88,8 +150,35 @@ func (_m *AgentService_DataClient) Context() context.Context {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Header provides a mock function with given fields:
|
||||
func (_m *AgentService_DataClient) Header() (metadata.MD, error) {
|
||||
// AgentService_DataClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context'
|
||||
type AgentService_DataClient_Context_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Context is a helper method to define mock.On call
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) Context() *AgentService_DataClient_Context_Call[Req, Res] {
|
||||
return &AgentService_DataClient_Context_Call[Req, Res]{Call: _e.mock.On("Context")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Context_Call[Req, Res]) Run(run func()) *AgentService_DataClient_Context_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Context_Call[Req, Res]) Return(_a0 context.Context) *AgentService_DataClient_Context_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Context_Call[Req, Res]) RunAndReturn(run func() context.Context) *AgentService_DataClient_Context_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Header provides a mock function with no fields
|
||||
func (_m *AgentService_DataClient[Req, Res]) Header() (metadata.MD, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -118,8 +207,35 @@ func (_m *AgentService_DataClient) Header() (metadata.MD, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AgentService_DataClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header'
|
||||
type AgentService_DataClient_Header_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Header is a helper method to define mock.On call
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) Header() *AgentService_DataClient_Header_Call[Req, Res] {
|
||||
return &AgentService_DataClient_Header_Call[Req, Res]{Call: _e.mock.On("Header")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Header_Call[Req, Res]) Run(run func()) *AgentService_DataClient_Header_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Header_Call[Req, Res]) Return(_a0 metadata.MD, _a1 error) *AgentService_DataClient_Header_Call[Req, Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Header_Call[Req, Res]) RunAndReturn(run func() (metadata.MD, error)) *AgentService_DataClient_Header_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RecvMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_DataClient) RecvMsg(m interface{}) error {
|
||||
func (_m *AgentService_DataClient[Req, Res]) RecvMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -136,8 +252,36 @@ func (_m *AgentService_DataClient) RecvMsg(m interface{}) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_DataClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg'
|
||||
type AgentService_DataClient_RecvMsg_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RecvMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) RecvMsg(m interface{}) *AgentService_DataClient_RecvMsg_Call[Req, Res] {
|
||||
return &AgentService_DataClient_RecvMsg_Call[Req, Res]{Call: _e.mock.On("RecvMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_RecvMsg_Call[Req, Res]) Run(run func(m interface{})) *AgentService_DataClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_RecvMsg_Call[Req, Res]) Return(_a0 error) *AgentService_DataClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_RecvMsg_Call[Req, Res]) RunAndReturn(run func(interface{}) error) *AgentService_DataClient_RecvMsg_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Send provides a mock function with given fields: _a0
|
||||
func (_m *AgentService_DataClient) Send(_a0 *agent.DataRequest) error {
|
||||
func (_m *AgentService_DataClient[Req, Res]) Send(_a0 *agent.DataRequest) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -154,8 +298,36 @@ func (_m *AgentService_DataClient) Send(_a0 *agent.DataRequest) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_DataClient_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send'
|
||||
type AgentService_DataClient_Send_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Send is a helper method to define mock.On call
|
||||
// - _a0 *agent.DataRequest
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) Send(_a0 interface{}) *AgentService_DataClient_Send_Call[Req, Res] {
|
||||
return &AgentService_DataClient_Send_Call[Req, Res]{Call: _e.mock.On("Send", _a0)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Send_Call[Req, Res]) Run(run func(_a0 *agent.DataRequest)) *AgentService_DataClient_Send_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(*agent.DataRequest))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Send_Call[Req, Res]) Return(_a0 error) *AgentService_DataClient_Send_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Send_Call[Req, Res]) RunAndReturn(run func(*agent.DataRequest) error) *AgentService_DataClient_Send_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_DataClient) SendMsg(m interface{}) error {
|
||||
func (_m *AgentService_DataClient[Req, Res]) SendMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -172,8 +344,36 @@ func (_m *AgentService_DataClient) SendMsg(m interface{}) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with given fields:
|
||||
func (_m *AgentService_DataClient) Trailer() metadata.MD {
|
||||
// AgentService_DataClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg'
|
||||
type AgentService_DataClient_SendMsg_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) SendMsg(m interface{}) *AgentService_DataClient_SendMsg_Call[Req, Res] {
|
||||
return &AgentService_DataClient_SendMsg_Call[Req, Res]{Call: _e.mock.On("SendMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_SendMsg_Call[Req, Res]) Run(run func(m interface{})) *AgentService_DataClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_SendMsg_Call[Req, Res]) Return(_a0 error) *AgentService_DataClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_SendMsg_Call[Req, Res]) RunAndReturn(run func(interface{}) error) *AgentService_DataClient_SendMsg_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with no fields
|
||||
func (_m *AgentService_DataClient[Req, Res]) Trailer() metadata.MD {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
@@ -192,13 +392,40 @@ func (_m *AgentService_DataClient) Trailer() metadata.MD {
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_DataClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer'
|
||||
type AgentService_DataClient_Trailer_Call[Req interface{}, Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Trailer is a helper method to define mock.On call
|
||||
func (_e *AgentService_DataClient_Expecter[Req, Res]) Trailer() *AgentService_DataClient_Trailer_Call[Req, Res] {
|
||||
return &AgentService_DataClient_Trailer_Call[Req, Res]{Call: _e.mock.On("Trailer")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Trailer_Call[Req, Res]) Run(run func()) *AgentService_DataClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Trailer_Call[Req, Res]) Return(_a0 metadata.MD) *AgentService_DataClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_DataClient_Trailer_Call[Req, Res]) RunAndReturn(run func() metadata.MD) *AgentService_DataClient_Trailer_Call[Req, Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewAgentService_DataClient creates a new instance of AgentService_DataClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAgentService_DataClient(t interface {
|
||||
func NewAgentService_DataClient[Req interface{}, Res interface{}](t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AgentService_DataClient {
|
||||
mock := &AgentService_DataClient{}
|
||||
}) *AgentService_DataClient[Req, Res] {
|
||||
mock := &AgentService_DataClient[Req, Res]{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
@@ -0,0 +1,388 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
agent "github.com/ultravioletrs/cocos/agent"
|
||||
|
||||
metadata "google.golang.org/grpc/metadata"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// AgentService_IMAMeasurementsClient is an autogenerated mock type for the AgentService_IMAMeasurementsClient type
|
||||
type AgentService_IMAMeasurementsClient[Res interface{}] struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AgentService_IMAMeasurementsClient_Expecter[Res interface{}] struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) EXPECT() *AgentService_IMAMeasurementsClient_Expecter[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_Expecter[Res]{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with no fields
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) CloseSend() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CloseSend")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend'
|
||||
type AgentService_IMAMeasurementsClient_CloseSend_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CloseSend is a helper method to define mock.On call
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) CloseSend() *AgentService_IMAMeasurementsClient_CloseSend_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_CloseSend_Call[Res]{Call: _e.mock.On("CloseSend")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_CloseSend_Call[Res]) Run(run func()) *AgentService_IMAMeasurementsClient_CloseSend_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_CloseSend_Call[Res]) Return(_a0 error) *AgentService_IMAMeasurementsClient_CloseSend_Call[Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_CloseSend_Call[Res]) RunAndReturn(run func() error) *AgentService_IMAMeasurementsClient_CloseSend_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Context provides a mock function with no fields
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) Context() context.Context {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Context")
|
||||
}
|
||||
|
||||
var r0 context.Context
|
||||
if rf, ok := ret.Get(0).(func() context.Context); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(context.Context)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context'
|
||||
type AgentService_IMAMeasurementsClient_Context_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Context is a helper method to define mock.On call
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) Context() *AgentService_IMAMeasurementsClient_Context_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_Context_Call[Res]{Call: _e.mock.On("Context")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Context_Call[Res]) Run(run func()) *AgentService_IMAMeasurementsClient_Context_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Context_Call[Res]) Return(_a0 context.Context) *AgentService_IMAMeasurementsClient_Context_Call[Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Context_Call[Res]) RunAndReturn(run func() context.Context) *AgentService_IMAMeasurementsClient_Context_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Header provides a mock function with no fields
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) Header() (metadata.MD, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Header")
|
||||
}
|
||||
|
||||
var r0 metadata.MD
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func() (metadata.MD, error)); ok {
|
||||
return rf()
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(metadata.MD)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header'
|
||||
type AgentService_IMAMeasurementsClient_Header_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Header is a helper method to define mock.On call
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) Header() *AgentService_IMAMeasurementsClient_Header_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_Header_Call[Res]{Call: _e.mock.On("Header")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Header_Call[Res]) Run(run func()) *AgentService_IMAMeasurementsClient_Header_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Header_Call[Res]) Return(_a0 metadata.MD, _a1 error) *AgentService_IMAMeasurementsClient_Header_Call[Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Header_Call[Res]) RunAndReturn(run func() (metadata.MD, error)) *AgentService_IMAMeasurementsClient_Header_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Recv provides a mock function with no fields
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) Recv() (*agent.IMAMeasurementsResponse, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Recv")
|
||||
}
|
||||
|
||||
var r0 *agent.IMAMeasurementsResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func() (*agent.IMAMeasurementsResponse, error)); ok {
|
||||
return rf()
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func() *agent.IMAMeasurementsResponse); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*agent.IMAMeasurementsResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_Recv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Recv'
|
||||
type AgentService_IMAMeasurementsClient_Recv_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Recv is a helper method to define mock.On call
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) Recv() *AgentService_IMAMeasurementsClient_Recv_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_Recv_Call[Res]{Call: _e.mock.On("Recv")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Recv_Call[Res]) Run(run func()) *AgentService_IMAMeasurementsClient_Recv_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Recv_Call[Res]) Return(_a0 *agent.IMAMeasurementsResponse, _a1 error) *AgentService_IMAMeasurementsClient_Recv_Call[Res] {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Recv_Call[Res]) RunAndReturn(run func() (*agent.IMAMeasurementsResponse, error)) *AgentService_IMAMeasurementsClient_Recv_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RecvMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) RecvMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RecvMsg")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
|
||||
r0 = rf(m)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg'
|
||||
type AgentService_IMAMeasurementsClient_RecvMsg_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RecvMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) RecvMsg(m interface{}) *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_RecvMsg_Call[Res]{Call: _e.mock.On("RecvMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res]) Run(run func(m interface{})) *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res]) Return(_a0 error) *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res]) RunAndReturn(run func(interface{}) error) *AgentService_IMAMeasurementsClient_RecvMsg_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendMsg provides a mock function with given fields: m
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) SendMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SendMsg")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
|
||||
r0 = rf(m)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg'
|
||||
type AgentService_IMAMeasurementsClient_SendMsg_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendMsg is a helper method to define mock.On call
|
||||
// - m interface{}
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) SendMsg(m interface{}) *AgentService_IMAMeasurementsClient_SendMsg_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_SendMsg_Call[Res]{Call: _e.mock.On("SendMsg", m)}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_SendMsg_Call[Res]) Run(run func(m interface{})) *AgentService_IMAMeasurementsClient_SendMsg_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_SendMsg_Call[Res]) Return(_a0 error) *AgentService_IMAMeasurementsClient_SendMsg_Call[Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_SendMsg_Call[Res]) RunAndReturn(run func(interface{}) error) *AgentService_IMAMeasurementsClient_SendMsg_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with no fields
|
||||
func (_m *AgentService_IMAMeasurementsClient[Res]) Trailer() metadata.MD {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Trailer")
|
||||
}
|
||||
|
||||
var r0 metadata.MD
|
||||
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(metadata.MD)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AgentService_IMAMeasurementsClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer'
|
||||
type AgentService_IMAMeasurementsClient_Trailer_Call[Res interface{}] struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Trailer is a helper method to define mock.On call
|
||||
func (_e *AgentService_IMAMeasurementsClient_Expecter[Res]) Trailer() *AgentService_IMAMeasurementsClient_Trailer_Call[Res] {
|
||||
return &AgentService_IMAMeasurementsClient_Trailer_Call[Res]{Call: _e.mock.On("Trailer")}
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Trailer_Call[Res]) Run(run func()) *AgentService_IMAMeasurementsClient_Trailer_Call[Res] {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Trailer_Call[Res]) Return(_a0 metadata.MD) *AgentService_IMAMeasurementsClient_Trailer_Call[Res] {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AgentService_IMAMeasurementsClient_Trailer_Call[Res]) RunAndReturn(run func() metadata.MD) *AgentService_IMAMeasurementsClient_Trailer_Call[Res] {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewAgentService_IMAMeasurementsClient creates a new instance of AgentService_IMAMeasurementsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAgentService_IMAMeasurementsClient[Res interface{}](t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AgentService_IMAMeasurementsClient[Res] {
|
||||
mock := &AgentService_IMAMeasurementsClient[Res]{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
+99
-40
@@ -6,15 +6,15 @@ package agent
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-sev-guest/client"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/ultravioletrs/cocos/agent/algorithm"
|
||||
"github.com/ultravioletrs/cocos/agent/algorithm/binary"
|
||||
"github.com/ultravioletrs/cocos/agent/algorithm/docker"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/ultravioletrs/cocos/agent/events"
|
||||
"github.com/ultravioletrs/cocos/agent/statemachine"
|
||||
"github.com/ultravioletrs/cocos/internal"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"golang.org/x/crypto/sha3"
|
||||
@@ -75,6 +75,11 @@ const (
|
||||
algoFilePermission = 0o700
|
||||
)
|
||||
|
||||
const (
|
||||
ImaMeasurementsFilePath = "/sys/kernel/security/integrity/ima/ascii_runtime_measurements"
|
||||
ImaPcrIndex = 10
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMalformedEntity indicates malformed entity specification (e.g.
|
||||
// invalid username or password).
|
||||
@@ -100,6 +105,10 @@ var (
|
||||
ErrAllResultsConsumed = errors.New("all results have been consumed by declared consumers")
|
||||
// ErrAttestationFailed attestation failed.
|
||||
ErrAttestationFailed = errors.New("failed to get raw quote")
|
||||
// ErrAttestationVTpmFailed vTPM attestation failed.
|
||||
ErrAttestationVTpmFailed = errors.New("failed to get vTPM quote")
|
||||
// ErrFetchAzureToken azure token fetch failed.
|
||||
ErrFetchAzureToken = errors.New("failed to get azure token")
|
||||
// ErrAttType indicates that the attestation type that is requested does not exist or is not supported.
|
||||
ErrAttestationType = errors.New("attestation type does not exist or is not supported")
|
||||
)
|
||||
@@ -112,38 +121,40 @@ type Service interface {
|
||||
Algo(ctx context.Context, algorithm Algorithm) error
|
||||
Data(ctx context.Context, dataset Dataset) error
|
||||
Result(ctx context.Context) ([]byte, error)
|
||||
Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType config.AttestationType) ([]byte, error)
|
||||
Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error)
|
||||
IMAMeasurements(ctx context.Context) ([]byte, []byte, error)
|
||||
AttestationResult(ctx context.Context, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error)
|
||||
State() string
|
||||
}
|
||||
|
||||
type agentService struct {
|
||||
mu sync.Mutex
|
||||
computation Computation // Holds the current computation request details.
|
||||
algorithm algorithm.Algorithm // Filepath to the algorithm received for the computation.
|
||||
result []byte // Stores the result of the computation.
|
||||
sm statemachine.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.
|
||||
quoteProvider client.LeveledQuoteProvider // Provider for generating attestation quotes.
|
||||
logger *slog.Logger // Logger for the agent service.
|
||||
resultsConsumed bool // Indicates if the results have been consumed.
|
||||
cancel context.CancelFunc // Cancels the computation context.
|
||||
vmpl int // VMPL at which the Agent is running.
|
||||
computation Computation // Holds the current computation request details.
|
||||
algorithm algorithm.Algorithm // Filepath to the algorithm received for the computation.
|
||||
result []byte // Stores the result of the computation.
|
||||
sm statemachine.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.
|
||||
provider attestation.Provider // Provider for generating attestation quotes.
|
||||
logger *slog.Logger // Logger for the agent service.
|
||||
resultsConsumed bool // Indicates if the results have been consumed.
|
||||
cancel context.CancelFunc // Cancels the computation context.
|
||||
vmpl int // VMPL at which the Agent is running.
|
||||
}
|
||||
|
||||
var _ Service = (*agentService)(nil)
|
||||
|
||||
// New instantiates the agent service implementation.
|
||||
func New(ctx context.Context, logger *slog.Logger, eventSvc events.Service, quoteProvider client.LeveledQuoteProvider, vmlp int) Service {
|
||||
func New(ctx context.Context, logger *slog.Logger, eventSvc events.Service, provider attestation.Provider, vmlp int) Service {
|
||||
sm := statemachine.NewStateMachine(Idle)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
svc := &agentService{
|
||||
sm: sm,
|
||||
eventSvc: eventSvc,
|
||||
quoteProvider: quoteProvider,
|
||||
logger: logger,
|
||||
cancel: cancel,
|
||||
vmpl: vmlp,
|
||||
sm: sm,
|
||||
eventSvc: eventSvc,
|
||||
provider: provider,
|
||||
logger: logger,
|
||||
cancel: cancel,
|
||||
vmpl: vmlp,
|
||||
}
|
||||
|
||||
transitions := []statemachine.Transition{
|
||||
@@ -173,8 +184,12 @@ func New(ctx context.Context, logger *slog.Logger, eventSvc events.Service, quot
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
sm.SendEvent(Start)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
@@ -213,14 +228,16 @@ func (as *agentService) StopComputation(ctx context.Context) error {
|
||||
as.mu.Lock()
|
||||
defer as.mu.Unlock()
|
||||
|
||||
as.eventSvc.SendEvent(as.computation.ID, "Stopped", "Stopped", json.RawMessage{})
|
||||
|
||||
as.cancel()
|
||||
|
||||
if err := as.algorithm.Stop(); err != nil {
|
||||
return fmt.Errorf("error stopping computation: %v", err)
|
||||
if as.algorithm != nil {
|
||||
if err := as.algorithm.Stop(); err != nil {
|
||||
return fmt.Errorf("error stopping computation: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
sm := statemachine.NewStateMachine(Idle)
|
||||
|
||||
if err := os.RemoveAll(algorithm.DatasetsDir); err != nil {
|
||||
return fmt.Errorf("error removing datasets directory: %v", err)
|
||||
}
|
||||
@@ -229,13 +246,28 @@ func (as *agentService) StopComputation(ctx context.Context) error {
|
||||
return fmt.Errorf("error removing results directory: %v", err)
|
||||
}
|
||||
|
||||
as.sm = sm
|
||||
as.sm.Reset(Idle)
|
||||
|
||||
as.computation = Computation{}
|
||||
as.algorithm = nil
|
||||
as.result = nil
|
||||
as.runError = nil
|
||||
as.resultsConsumed = false
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
as.cancel = cancel
|
||||
|
||||
go func() {
|
||||
if err := as.sm.Start(ctx); err != nil {
|
||||
as.logger.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
as.sm.SendEvent(Start)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -402,26 +434,39 @@ func (as *agentService) Result(ctx context.Context) ([]byte, error) {
|
||||
return as.result, as.runError
|
||||
}
|
||||
|
||||
func (as *agentService) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType config.AttestationType) ([]byte, error) {
|
||||
func (as *agentService) Attestation(ctx context.Context, reportData [quoteprovider.Nonce]byte, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
switch attType {
|
||||
case config.SNP:
|
||||
rawQuote, err := as.quoteProvider.GetRawQuoteAtLevel(reportData, uint(as.vmpl))
|
||||
case attestation.SNP, attestation.TDX:
|
||||
rawQuote, err := as.provider.TeeAttestation(reportData[:])
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
return []byte{}, errors.Wrap(ErrAttestationFailed, err)
|
||||
}
|
||||
return rawQuote, nil
|
||||
case config.VTPM:
|
||||
vTPMQuote, err := vtpm.Attest(reportData[:], nonce[:], false)
|
||||
case attestation.VTPM:
|
||||
vTPMQuote, err := as.provider.VTpmAttestation(nonce[:])
|
||||
if err != nil {
|
||||
return []byte{}, errors.Wrap(ErrAttestationVTpmFailed, err)
|
||||
}
|
||||
return vTPMQuote, nil
|
||||
case attestation.SNPvTPM:
|
||||
vTPMQuote, err := as.provider.Attestation(reportData[:], nonce[:])
|
||||
if err != nil {
|
||||
return []byte{}, errors.Wrap(ErrAttestationVTpmFailed, err)
|
||||
}
|
||||
return vTPMQuote, nil
|
||||
default:
|
||||
return []byte{}, ErrAttestationType
|
||||
}
|
||||
}
|
||||
|
||||
func (as *agentService) AttestationResult(ctx context.Context, nonce [vtpm.Nonce]byte, attType attestation.PlatformType) ([]byte, error) {
|
||||
switch attType {
|
||||
case attestation.AzureToken:
|
||||
token, err := as.provider.AzureAttestationToken(nonce[:])
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return vTPMQuote, nil
|
||||
case config.SNPvTPM:
|
||||
vTPMQuote, err := vtpm.Attest(reportData[:], nonce[:], true)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return vTPMQuote, nil
|
||||
return token, nil
|
||||
default:
|
||||
return []byte{}, ErrAttestationType
|
||||
}
|
||||
@@ -480,3 +525,17 @@ func (as *agentService) publishEvent(status string) statemachine.Action {
|
||||
as.eventSvc.SendEvent(as.computation.ID, state.String(), status, json.RawMessage{})
|
||||
}
|
||||
}
|
||||
|
||||
func (as *agentService) IMAMeasurements(ctx context.Context) ([]byte, []byte, error) {
|
||||
data, err := os.ReadFile(ImaMeasurementsFilePath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error reading Linux IMA measurements file: %s", err.Error())
|
||||
}
|
||||
|
||||
pcr10, err := vtpm.GetPCRSHA1Value(ImaPcrIndex)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error reading TPM PCR #10: %s", err.Error())
|
||||
}
|
||||
|
||||
return data, pcr10, nil
|
||||
}
|
||||
|
||||
+300
-26
@@ -5,23 +5,27 @@ package agent
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
mglog "github.com/absmach/magistrala/logger"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/agent/algorithm"
|
||||
algomocks "github.com/ultravioletrs/cocos/agent/algorithm/mocks"
|
||||
"github.com/ultravioletrs/cocos/agent/algorithm/python"
|
||||
"github.com/ultravioletrs/cocos/agent/events/mocks"
|
||||
"github.com/ultravioletrs/cocos/agent/statemachine"
|
||||
smmocks "github.com/ultravioletrs/cocos/agent/statemachine/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
mocks2 "github.com/ultravioletrs/cocos/pkg/attestation/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
mocks2 "github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -36,13 +40,11 @@ var (
|
||||
const datasetFile = "iris.csv"
|
||||
|
||||
func TestAlgo(t *testing.T) {
|
||||
qp, err := quoteprovider.GetLeveledQuoteProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
algo, err := os.ReadFile(algoPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
algoHash := sha3.Sum256(algo)
|
||||
vtpm.ExternalTPM = &vtpm.DummyRWC{}
|
||||
|
||||
reqFile, err := os.ReadFile(reqPath)
|
||||
require.NoError(t, err)
|
||||
@@ -121,7 +123,7 @@ func TestAlgo(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
svc := New(ctx, mglog.NewMock(), events, qp, 0)
|
||||
svc := New(ctx, mglog.NewMock(), events, &attestation.EmptyProvider{}, 0)
|
||||
|
||||
err := svc.InitComputation(ctx, testComputation(t))
|
||||
require.NoError(t, err)
|
||||
@@ -140,13 +142,11 @@ func TestAlgo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestData(t *testing.T) {
|
||||
qp, err := quoteprovider.GetLeveledQuoteProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
algo, err := os.ReadFile(algoPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
algoHash := sha3.Sum256(algo)
|
||||
vtpm.ExternalTPM = &vtpm.DummyRWC{}
|
||||
|
||||
alg := Algorithm{
|
||||
Hash: algoHash,
|
||||
@@ -216,7 +216,7 @@ func TestData(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, qp, 0)
|
||||
svc := New(ctx, mglog.NewMock(), events, &attestation.EmptyProvider{}, 0)
|
||||
|
||||
err := svc.InitComputation(ctx, testComputation(t))
|
||||
require.NoError(t, err)
|
||||
@@ -241,9 +241,6 @@ func TestData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResult(t *testing.T) {
|
||||
qp, err := quoteprovider.GetLeveledQuoteProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
err error
|
||||
@@ -301,10 +298,10 @@ func TestResult(t *testing.T) {
|
||||
sm.On("SendEvent", mock.Anything).Return()
|
||||
|
||||
svc := &agentService{
|
||||
sm: sm,
|
||||
eventSvc: events,
|
||||
quoteProvider: qp,
|
||||
computation: testComputation(t),
|
||||
sm: sm,
|
||||
eventSvc: events,
|
||||
provider: &attestation.EmptyProvider{},
|
||||
computation: testComputation(t),
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -324,29 +321,64 @@ func TestResult(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttestation(t *testing.T) {
|
||||
qp := new(mocks2.LeveledQuoteProvider)
|
||||
provider := new(mocks2.Provider)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
reportData [quoteprovider.Nonce]byte
|
||||
nonce [vtpm.Nonce]byte
|
||||
rawQuote []uint8
|
||||
platform attestation.PlatformType
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Test attestation successful",
|
||||
name: "Test SNP attestation successful",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: make([]uint8, 0),
|
||||
platform: attestation.SNP,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Test attestation failed",
|
||||
name: "Test SNP attestation failed",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: nil,
|
||||
platform: attestation.SNP,
|
||||
err: ErrAttestationFailed,
|
||||
},
|
||||
{
|
||||
name: "Test vTPM attestation successful",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: make([]uint8, 0),
|
||||
platform: attestation.VTPM,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Test vTPM attestation failed",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: nil,
|
||||
platform: attestation.VTPM,
|
||||
err: ErrAttestationVTpmFailed,
|
||||
},
|
||||
{
|
||||
name: "Test SNP-vTPM attestation successful",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: make([]uint8, 0),
|
||||
platform: attestation.SNPvTPM,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Test SNP-vTPM attestation failed",
|
||||
reportData: generateReportData(),
|
||||
nonce: [32]byte{},
|
||||
rawQuote: nil,
|
||||
platform: attestation.SNPvTPM,
|
||||
err: ErrAttestationVTpmFailed,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -359,20 +391,78 @@ func TestAttestation(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
getQuote := qp.On("GetRawQuoteAtLevel", mock.Anything, mock.Anything).Return(tc.rawQuote, tc.err)
|
||||
if tc.err != ErrAttestationFailed {
|
||||
getQuote = qp.On("GetRawQuoteAtLevel", mock.Anything, mock.Anything).Return(tc.nonce, nil)
|
||||
getQuote := provider.On("TeeAttestation", mock.Anything).Return(tc.rawQuote, tc.err)
|
||||
vtpmQuote := provider.On("VTpmAttestation", mock.Anything).Return(tc.rawQuote, tc.err)
|
||||
snpVtpm := provider.On("Attestation", mock.Anything, mock.Anything).Return(tc.rawQuote, tc.err)
|
||||
if tc.err != ErrAttestationFailed && tc.err != ErrAttestationVTpmFailed {
|
||||
getQuote = provider.On("TeeAttestation", mock.Anything).Return(tc.nonce, nil)
|
||||
vtpmQuote = provider.On("VTpmAttestation", mock.Anything).Return(tc.nonce[:], nil)
|
||||
snpVtpm = provider.On("Attestation", mock.Anything, mock.Anything).Return(tc.nonce[:], nil)
|
||||
}
|
||||
defer getQuote.Unset()
|
||||
defer vtpmQuote.Unset()
|
||||
defer snpVtpm.Unset()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, qp, 0)
|
||||
svc := New(ctx, mglog.NewMock(), events, provider, 0)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
_, err := svc.Attestation(ctx, tc.reportData, tc.nonce, 0)
|
||||
_, err := svc.Attestation(ctx, tc.reportData, tc.nonce, tc.platform)
|
||||
assert.True(t, errors.Contains(err, tc.err), "expected %v, got %v", tc.err, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationResult(t *testing.T) {
|
||||
provider := new(mocks2.Provider)
|
||||
cases := []struct {
|
||||
name string
|
||||
nonce [vtpm.Nonce]byte
|
||||
platform attestation.PlatformType
|
||||
token []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Azure token fetch successful",
|
||||
nonce: [32]byte{1, 2, 3}, // any test nonce
|
||||
platform: attestation.AzureToken,
|
||||
token: []byte("mockToken"),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Azure token fetch failed",
|
||||
nonce: [32]byte{4, 5, 6},
|
||||
platform: attestation.AzureToken,
|
||||
token: []byte{},
|
||||
err: ErrFetchAzureToken,
|
||||
},
|
||||
{
|
||||
name: "Invalid attestation type",
|
||||
nonce: [32]byte{7, 8, 9},
|
||||
platform: attestation.SNP,
|
||||
token: []byte{},
|
||||
err: ErrAttestationType,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
events := new(mocks.Service)
|
||||
events.EXPECT().SendEvent(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
|
||||
|
||||
if tc.platform == attestation.AzureToken {
|
||||
provider.On("AzureAttestationToken", tc.nonce[:]).Return(tc.token, tc.err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, provider, 0)
|
||||
|
||||
result, err := svc.AttestationResult(ctx, tc.nonce, tc.platform)
|
||||
assert.True(t, errors.Contains(err, tc.err), "expected error %v, got %v", tc.err, err)
|
||||
assert.Equal(t, tc.token, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateReportData() [quoteprovider.Nonce]byte {
|
||||
bytes := make([]byte, quoteprovider.Nonce)
|
||||
_, err := rand.Read(bytes)
|
||||
@@ -402,3 +492,187 @@ func testComputation(t *testing.T) Computation {
|
||||
ResultConsumers: []ResultConsumer{{UserKey: []byte("key")}},
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopComputation(t *testing.T) {
|
||||
testDataDir := "test_datasets"
|
||||
testResultsDir := "test_results"
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
setupDirs bool
|
||||
setupAlgo bool
|
||||
algoStopErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "Stop computation successfully",
|
||||
setupDirs: true,
|
||||
setupAlgo: true,
|
||||
algoStopErr: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Stop computation with algorithm stop error",
|
||||
setupDirs: true,
|
||||
setupAlgo: true,
|
||||
algoStopErr: fmt.Errorf("algorithm stop failed"),
|
||||
expectedErr: fmt.Errorf("error stopping computation: algorithm stop failed"),
|
||||
},
|
||||
{
|
||||
name: "Stop computation without algorithm",
|
||||
setupDirs: true,
|
||||
setupAlgo: false,
|
||||
algoStopErr: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Stop computation with missing directories",
|
||||
setupDirs: false,
|
||||
setupAlgo: false,
|
||||
algoStopErr: nil,
|
||||
expectedErr: nil, // os.RemoveAll doesn't error on non-existing directories
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
events := new(mocks.Service)
|
||||
events.On("SendEvent", mock.Anything, "Stopped", "Stopped", mock.Anything).Return()
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, &attestation.EmptyProvider{}, 0).(*agentService)
|
||||
|
||||
svc.computation = Computation{
|
||||
ID: "test-computation",
|
||||
Name: "test",
|
||||
}
|
||||
|
||||
if tc.setupDirs {
|
||||
err := os.MkdirAll(testDataDir, 0o755)
|
||||
require.NoError(t, err)
|
||||
err = os.MkdirAll(testResultsDir, 0o755)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tc.setupAlgo {
|
||||
mockAlgo := new(algomocks.Algorithm)
|
||||
mockAlgo.On("Stop").Return(tc.algoStopErr)
|
||||
svc.algorithm = mockAlgo
|
||||
}
|
||||
|
||||
err := svc.StopComputation(ctx)
|
||||
|
||||
if tc.expectedErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tc.expectedErr.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, ReceivingManifest, svc.sm.GetState())
|
||||
assert.Nil(t, svc.result)
|
||||
assert.Nil(t, svc.runError)
|
||||
assert.False(t, svc.resultsConsumed)
|
||||
|
||||
events.AssertExpectations(t)
|
||||
|
||||
_ = os.RemoveAll(testDataDir)
|
||||
_ = os.RemoveAll(testResultsDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopComputationIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
algo := []byte("#!/bin/bash\necho 'test algorithm'")
|
||||
algoHash := sha3.Sum256(algo)
|
||||
|
||||
testDir := "test_integration"
|
||||
err := os.MkdirAll(testDir, 0o755)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
algoFile := filepath.Join(testDir, "test_algo")
|
||||
err = os.WriteFile(algoFile, algo, 0o755)
|
||||
require.NoError(t, err)
|
||||
|
||||
events := new(mocks.Service)
|
||||
events.On("SendEvent", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
|
||||
|
||||
ctx := metadata.NewIncomingContext(context.Background(),
|
||||
metadata.Pairs(algorithm.AlgoTypeKey, "bin"),
|
||||
)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, &attestation.EmptyProvider{}, 0)
|
||||
|
||||
computation := Computation{
|
||||
ID: "integration-test",
|
||||
Name: "Integration Test",
|
||||
Algorithm: Algorithm{
|
||||
Hash: algoHash,
|
||||
Algorithm: algo,
|
||||
},
|
||||
}
|
||||
|
||||
err = svc.InitComputation(ctx, computation)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
err = svc.Algo(ctx, Algorithm{
|
||||
Hash: algoHash,
|
||||
Algorithm: algo,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
err = svc.StopComputation(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "ReceivingManifest", svc.State())
|
||||
}
|
||||
|
||||
func TestStopComputationConcurrent(t *testing.T) {
|
||||
events := new(mocks.Service)
|
||||
events.On("SendEvent", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
svc := New(ctx, mglog.NewMock(), events, &attestation.EmptyProvider{}, 0)
|
||||
|
||||
svc.(*agentService).computation = Computation{
|
||||
ID: "concurrent-test",
|
||||
Name: "Concurrent Test",
|
||||
}
|
||||
|
||||
const numGoroutines = 10
|
||||
errChan := make(chan error, numGoroutines)
|
||||
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
go func() {
|
||||
err := svc.StopComputation(ctx)
|
||||
errChan <- err
|
||||
}()
|
||||
}
|
||||
|
||||
var errors []error
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
err := <-errChan
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, len(errors) < numGoroutines, "All StopComputation calls failed")
|
||||
}
|
||||
|
||||
+9
-3
@@ -54,10 +54,11 @@ func TestAddTransition(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
if err := sm.Start(ctx); err != context.Canceled {
|
||||
t.Errorf("Start returned error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
sm.SendEvent(Event1)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
@@ -79,7 +80,7 @@ func TestSetAction(t *testing.T) {
|
||||
|
||||
sm.AddTransition(statemachine.Transition{From: State1, Event: Event1, To: State2})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 150*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
@@ -88,8 +89,12 @@ func TestSetAction(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
sm.SendEvent(Event1)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if ctx.Err() != nil {
|
||||
@@ -132,10 +137,11 @@ func TestMultipleTransitions(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
if err := sm.Start(ctx); err != context.Canceled {
|
||||
t.Errorf("Start returned error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
transitions := []struct {
|
||||
event MockEvent
|
||||
want MockState
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -54,11 +54,11 @@ func (_c *StateMachine_AddTransition_Call) Return() *StateMachine_AddTransition_
|
||||
}
|
||||
|
||||
func (_c *StateMachine_AddTransition_Call) RunAndReturn(run func(statemachine.Transition)) *StateMachine_AddTransition_Call {
|
||||
_c.Call.Return(run)
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetState provides a mock function with given fields:
|
||||
// GetState provides a mock function with no fields
|
||||
func (_m *StateMachine) GetState() statemachine.State {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -105,6 +105,39 @@ func (_c *StateMachine_GetState_Call) RunAndReturn(run func() statemachine.State
|
||||
return _c
|
||||
}
|
||||
|
||||
// Reset provides a mock function with given fields: initialState
|
||||
func (_m *StateMachine) Reset(initialState statemachine.State) {
|
||||
_m.Called(initialState)
|
||||
}
|
||||
|
||||
// StateMachine_Reset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reset'
|
||||
type StateMachine_Reset_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Reset is a helper method to define mock.On call
|
||||
// - initialState statemachine.State
|
||||
func (_e *StateMachine_Expecter) Reset(initialState interface{}) *StateMachine_Reset_Call {
|
||||
return &StateMachine_Reset_Call{Call: _e.mock.On("Reset", initialState)}
|
||||
}
|
||||
|
||||
func (_c *StateMachine_Reset_Call) Run(run func(initialState statemachine.State)) *StateMachine_Reset_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(statemachine.State))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *StateMachine_Reset_Call) Return() *StateMachine_Reset_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *StateMachine_Reset_Call) RunAndReturn(run func(statemachine.State)) *StateMachine_Reset_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SendEvent provides a mock function with given fields: event
|
||||
func (_m *StateMachine) SendEvent(event statemachine.Event) {
|
||||
_m.Called(event)
|
||||
@@ -134,7 +167,7 @@ func (_c *StateMachine_SendEvent_Call) Return() *StateMachine_SendEvent_Call {
|
||||
}
|
||||
|
||||
func (_c *StateMachine_SendEvent_Call) RunAndReturn(run func(statemachine.Event)) *StateMachine_SendEvent_Call {
|
||||
_c.Call.Return(run)
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -168,7 +201,7 @@ func (_c *StateMachine_SetAction_Call) Return() *StateMachine_SetAction_Call {
|
||||
}
|
||||
|
||||
func (_c *StateMachine_SetAction_Call) RunAndReturn(run func(statemachine.State, statemachine.Action)) *StateMachine_SetAction_Call {
|
||||
_c.Call.Return(run)
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ type StateMachine interface {
|
||||
GetState() State
|
||||
SendEvent(event Event)
|
||||
Start(ctx context.Context) error
|
||||
Reset(initialState State)
|
||||
}
|
||||
|
||||
type stateMachine struct {
|
||||
@@ -38,6 +39,7 @@ type stateMachine struct {
|
||||
transitions map[State]map[Event]State
|
||||
actions map[State]Action
|
||||
eventChan chan Event
|
||||
resetChan chan struct{}
|
||||
}
|
||||
|
||||
func NewStateMachine(initialState State) StateMachine {
|
||||
@@ -46,6 +48,7 @@ func NewStateMachine(initialState State) StateMachine {
|
||||
transitions: make(map[State]map[Event]State),
|
||||
actions: make(map[State]Action),
|
||||
eventChan: make(chan Event),
|
||||
resetChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,22 +76,54 @@ func (sm *stateMachine) GetState() State {
|
||||
}
|
||||
|
||||
func (sm *stateMachine) SendEvent(event Event) {
|
||||
sm.eventChan <- event
|
||||
sm.mu.Lock()
|
||||
eventChan := sm.eventChan
|
||||
sm.mu.Unlock()
|
||||
|
||||
select {
|
||||
case eventChan <- event:
|
||||
default:
|
||||
// Channel might be closed or full, ignore the event
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *stateMachine) Start(ctx context.Context) error {
|
||||
for {
|
||||
sm.mu.Lock()
|
||||
eventChan := sm.eventChan
|
||||
resetChan := sm.resetChan
|
||||
sm.mu.Unlock()
|
||||
|
||||
select {
|
||||
case event := <-sm.eventChan:
|
||||
case event := <-eventChan:
|
||||
if err := sm.handleEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-resetChan:
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *stateMachine) Reset(initialState State) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
// Reset current state to initial state
|
||||
sm.currentState = initialState
|
||||
|
||||
// Close the existing event channel to stop processing events
|
||||
close(sm.eventChan)
|
||||
|
||||
// Close the reset channel to signal Start() to restart
|
||||
close(sm.resetChan)
|
||||
|
||||
sm.eventChan = make(chan Event)
|
||||
sm.resetChan = make(chan struct{})
|
||||
}
|
||||
|
||||
func (sm *stateMachine) handleEvent(event Event) error {
|
||||
sm.mu.Lock()
|
||||
currentState := sm.currentState
|
||||
|
||||
@@ -0,0 +1,607 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package statemachine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testState string
|
||||
|
||||
func (s testState) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type testEvent string
|
||||
|
||||
func (e testEvent) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
const (
|
||||
StateIdle testState = "idle"
|
||||
StateRunning testState = "running"
|
||||
StatePaused testState = "paused"
|
||||
StateStopped testState = "stopped"
|
||||
StateError testState = "error"
|
||||
)
|
||||
|
||||
const (
|
||||
EventStart testEvent = "start"
|
||||
EventPause testEvent = "pause"
|
||||
EventStop testEvent = "stop"
|
||||
EventReset testEvent = "reset"
|
||||
EventError testEvent = "error"
|
||||
)
|
||||
|
||||
func TestNewStateMachine(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialState State
|
||||
want State
|
||||
}{
|
||||
{
|
||||
name: "create with idle state",
|
||||
initialState: StateIdle,
|
||||
want: StateIdle,
|
||||
},
|
||||
{
|
||||
name: "create with running state",
|
||||
initialState: StateRunning,
|
||||
want: StateRunning,
|
||||
},
|
||||
{
|
||||
name: "create with custom state",
|
||||
initialState: testState("custom"),
|
||||
want: testState("custom"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(tt.initialState)
|
||||
if got := sm.GetState(); got != tt.want {
|
||||
t.Errorf("NewStateMachine() initial state = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_AddTransition(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
transitions []Transition
|
||||
from State
|
||||
event Event
|
||||
expectTo State
|
||||
expectValid bool
|
||||
}{
|
||||
{
|
||||
name: "single transition",
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
from: StateIdle,
|
||||
event: EventStart,
|
||||
expectTo: StateRunning,
|
||||
expectValid: true,
|
||||
},
|
||||
{
|
||||
name: "multiple transitions from same state",
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
{From: StateIdle, Event: EventError, To: StateError},
|
||||
},
|
||||
from: StateIdle,
|
||||
event: EventError,
|
||||
expectTo: StateError,
|
||||
expectValid: true,
|
||||
},
|
||||
{
|
||||
name: "overwrite existing transition",
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
{From: StateIdle, Event: EventStart, To: StatePaused}, // Overwrite
|
||||
},
|
||||
from: StateIdle,
|
||||
event: EventStart,
|
||||
expectTo: StatePaused,
|
||||
expectValid: true,
|
||||
},
|
||||
{
|
||||
name: "transition not found",
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
from: StateRunning,
|
||||
event: EventPause,
|
||||
expectValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(StateIdle).(*stateMachine)
|
||||
|
||||
for _, transition := range tt.transitions {
|
||||
sm.AddTransition(transition)
|
||||
}
|
||||
|
||||
sm.mu.Lock()
|
||||
nextState, valid := sm.transitions[tt.from][tt.event]
|
||||
sm.mu.Unlock()
|
||||
|
||||
if valid != tt.expectValid {
|
||||
t.Errorf("Transition validity = %v, want %v", valid, tt.expectValid)
|
||||
}
|
||||
|
||||
if tt.expectValid && nextState != tt.expectTo {
|
||||
t.Errorf("Transition destination = %v, want %v", nextState, tt.expectTo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_SetAction(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state State
|
||||
action Action
|
||||
expectAction bool
|
||||
}{
|
||||
{
|
||||
name: "set action for state",
|
||||
state: StateRunning,
|
||||
action: func(s State) {
|
||||
},
|
||||
expectAction: true,
|
||||
},
|
||||
{
|
||||
name: "set nil action",
|
||||
state: StatePaused,
|
||||
action: nil,
|
||||
expectAction: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(StateIdle).(*stateMachine)
|
||||
sm.SetAction(tt.state, tt.action)
|
||||
|
||||
sm.mu.Lock()
|
||||
action := sm.actions[tt.state]
|
||||
sm.mu.Unlock()
|
||||
|
||||
if tt.expectAction && action == nil {
|
||||
t.Error("Expected action to be set, but it was nil")
|
||||
}
|
||||
if !tt.expectAction && action != nil {
|
||||
t.Error("Expected action to be nil, but it was set")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_GetState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialState State
|
||||
transitions []Transition
|
||||
events []Event
|
||||
finalState State
|
||||
}{
|
||||
{
|
||||
name: "get initial state",
|
||||
initialState: StateIdle,
|
||||
finalState: StateIdle,
|
||||
},
|
||||
{
|
||||
name: "get state after transition",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
events: []Event{EventStart},
|
||||
finalState: StateRunning,
|
||||
},
|
||||
{
|
||||
name: "get state after multiple transitions",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
{From: StateRunning, Event: EventPause, To: StatePaused},
|
||||
{From: StatePaused, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
events: []Event{EventStart, EventPause, EventStart},
|
||||
finalState: StateRunning,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(tt.initialState)
|
||||
|
||||
for _, transition := range tt.transitions {
|
||||
sm.AddTransition(transition)
|
||||
}
|
||||
|
||||
smImpl := sm.(*stateMachine)
|
||||
for _, event := range tt.events {
|
||||
if err := smImpl.handleEvent(event); err != nil {
|
||||
t.Fatalf("Failed to handle event %v: %v", event, err)
|
||||
}
|
||||
}
|
||||
|
||||
if got := sm.GetState(); got != tt.finalState {
|
||||
t.Errorf("GetState() = %v, want %v", got, tt.finalState)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_Start(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialState State
|
||||
transitions []Transition
|
||||
events []Event
|
||||
cancelAfter time.Duration
|
||||
expectError bool
|
||||
expectedStates []State
|
||||
}{
|
||||
{
|
||||
name: "start and cancel immediately",
|
||||
initialState: StateIdle,
|
||||
cancelAfter: 10 * time.Millisecond,
|
||||
expectError: true, // context.Canceled
|
||||
},
|
||||
{
|
||||
name: "process events then cancel",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
{From: StateRunning, Event: EventStop, To: StateStopped},
|
||||
},
|
||||
events: []Event{EventStart, EventStop},
|
||||
cancelAfter: 100 * time.Millisecond,
|
||||
expectError: true, // context.Canceled
|
||||
expectedStates: []State{StateRunning, StateStopped},
|
||||
},
|
||||
{
|
||||
name: "invalid transition error",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
events: []Event{EventPause}, // Invalid from StateIdle
|
||||
cancelAfter: 50 * time.Millisecond,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(tt.initialState)
|
||||
|
||||
for _, transition := range tt.transitions {
|
||||
sm.AddTransition(transition)
|
||||
}
|
||||
|
||||
var states []State
|
||||
var mu sync.Mutex
|
||||
|
||||
for _, state := range tt.expectedStates {
|
||||
sm.SetAction(state, func(s State) {
|
||||
mu.Lock()
|
||||
states = append(states, s)
|
||||
mu.Unlock()
|
||||
})
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- sm.Start(ctx)
|
||||
}()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
for _, event := range tt.events {
|
||||
sm.SendEvent(event)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(tt.cancelAfter)
|
||||
cancel()
|
||||
|
||||
err := <-errChan
|
||||
|
||||
if tt.expectError && err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if !tt.expectError && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
mu.Lock()
|
||||
if len(states) != len(tt.expectedStates) {
|
||||
t.Errorf("Expected %d state changes, got %d", len(tt.expectedStates), len(states))
|
||||
}
|
||||
for i, expectedState := range tt.expectedStates {
|
||||
if i < len(states) && states[i] != expectedState {
|
||||
t.Errorf("State change %d = %v, want %v", i, states[i], expectedState)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_Reset(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialState State
|
||||
resetState State
|
||||
setupTransitions []Transition
|
||||
eventsBeforeReset []Event
|
||||
eventsAfterReset []Event
|
||||
expectedState State
|
||||
}{
|
||||
{
|
||||
name: "reset to same state",
|
||||
initialState: StateIdle,
|
||||
resetState: StateIdle,
|
||||
expectedState: StateIdle,
|
||||
},
|
||||
{
|
||||
name: "reset to different state",
|
||||
initialState: StateIdle,
|
||||
resetState: StateRunning,
|
||||
expectedState: StateRunning,
|
||||
},
|
||||
{
|
||||
name: "reset after state changes",
|
||||
initialState: StateIdle,
|
||||
resetState: StateIdle,
|
||||
setupTransitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
eventsBeforeReset: []Event{EventStart},
|
||||
expectedState: StateIdle,
|
||||
},
|
||||
{
|
||||
name: "reset and send new events",
|
||||
initialState: StateIdle,
|
||||
resetState: StateIdle,
|
||||
setupTransitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
{From: StateRunning, Event: EventStop, To: StateStopped},
|
||||
},
|
||||
eventsBeforeReset: []Event{EventStart},
|
||||
eventsAfterReset: []Event{EventStart},
|
||||
expectedState: StateIdle,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(tt.initialState)
|
||||
smImpl := sm.(*stateMachine)
|
||||
|
||||
for _, transition := range tt.setupTransitions {
|
||||
sm.AddTransition(transition)
|
||||
}
|
||||
|
||||
for _, event := range tt.eventsBeforeReset {
|
||||
if err := smImpl.handleEvent(event); err != nil {
|
||||
// Ignore errors for this test
|
||||
}
|
||||
}
|
||||
|
||||
sm.Reset(tt.resetState)
|
||||
|
||||
if got := sm.GetState(); got != tt.expectedState {
|
||||
t.Errorf("State after reset = %v, want %v", got, tt.expectedState)
|
||||
}
|
||||
|
||||
for _, event := range tt.eventsAfterReset {
|
||||
sm.SendEvent(event)
|
||||
}
|
||||
|
||||
// For events after reset, we can't easily check the channel length
|
||||
// due to the synchronization changes, so we just verify the reset worked
|
||||
if len(tt.eventsAfterReset) > 0 {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_Reset_WithRunningStateMachine(t *testing.T) {
|
||||
sm := NewStateMachine(StateIdle)
|
||||
sm.AddTransition(Transition{From: StateIdle, Event: EventStart, To: StateRunning})
|
||||
sm.AddTransition(Transition{From: StateRunning, Event: EventStop, To: StateStopped})
|
||||
|
||||
var stateChanges []State
|
||||
var mu sync.Mutex
|
||||
|
||||
sm.SetAction(StateRunning, func(s State) {
|
||||
mu.Lock()
|
||||
stateChanges = append(stateChanges, s)
|
||||
mu.Unlock()
|
||||
})
|
||||
|
||||
sm.SetAction(StateStopped, func(s State) {
|
||||
mu.Lock()
|
||||
stateChanges = append(stateChanges, s)
|
||||
mu.Unlock()
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
if err := sm.Start(ctx); err != nil {
|
||||
}
|
||||
}()
|
||||
|
||||
// Give it time to start
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
// Send an event
|
||||
sm.SendEvent(EventStart)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Reset while running
|
||||
sm.Reset(StateIdle)
|
||||
|
||||
// Verify state was reset
|
||||
if got := sm.GetState(); got != StateIdle {
|
||||
t.Errorf("State after reset = %v, want %v", got, StateIdle)
|
||||
}
|
||||
|
||||
// Send another event after reset
|
||||
sm.SendEvent(EventStart)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
mu.Lock()
|
||||
changes := len(stateChanges)
|
||||
mu.Unlock()
|
||||
|
||||
// Should have at least processed the first event
|
||||
if changes < 1 {
|
||||
t.Errorf("Expected at least 1 state change, got %d", changes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_HandleEvent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialState State
|
||||
transitions []Transition
|
||||
event Event
|
||||
expectedState State
|
||||
expectError bool
|
||||
expectActionCall bool
|
||||
}{
|
||||
{
|
||||
name: "valid transition",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
event: EventStart,
|
||||
expectedState: StateRunning,
|
||||
expectError: false,
|
||||
expectActionCall: true,
|
||||
},
|
||||
{
|
||||
name: "invalid transition",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateRunning, Event: EventPause, To: StatePaused},
|
||||
},
|
||||
event: EventStart,
|
||||
expectedState: StateIdle,
|
||||
expectError: true,
|
||||
expectActionCall: false,
|
||||
},
|
||||
{
|
||||
name: "transition with no action",
|
||||
initialState: StateIdle,
|
||||
transitions: []Transition{
|
||||
{From: StateIdle, Event: EventStart, To: StateRunning},
|
||||
},
|
||||
event: EventStart,
|
||||
expectedState: StateRunning,
|
||||
expectError: false,
|
||||
expectActionCall: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := NewStateMachine(tt.initialState).(*stateMachine)
|
||||
|
||||
for _, transition := range tt.transitions {
|
||||
sm.AddTransition(transition)
|
||||
}
|
||||
|
||||
var actionCalled bool
|
||||
var mu sync.Mutex
|
||||
|
||||
if tt.expectActionCall {
|
||||
sm.SetAction(tt.expectedState, func(s State) {
|
||||
mu.Lock()
|
||||
actionCalled = true
|
||||
mu.Unlock()
|
||||
})
|
||||
}
|
||||
|
||||
err := sm.handleEvent(tt.event)
|
||||
|
||||
if tt.expectError && err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if !tt.expectError && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if sm.GetState() != tt.expectedState {
|
||||
t.Errorf("State after handleEvent = %v, want %v", sm.GetState(), tt.expectedState)
|
||||
}
|
||||
|
||||
if tt.expectActionCall {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mu.Lock()
|
||||
called := actionCalled
|
||||
mu.Unlock()
|
||||
if !called {
|
||||
t.Error("Expected action to be called but it wasn't")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMachine_SendEvent_ThreadSafety(t *testing.T) {
|
||||
sm := NewStateMachine(StateIdle)
|
||||
sm.AddTransition(Transition{From: StateIdle, Event: EventStart, To: StateRunning})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
if err := sm.Start(ctx); err != nil {
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
numGoroutines := 10
|
||||
eventsPerGoroutine := 100
|
||||
|
||||
// Send events concurrently
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < eventsPerGoroutine; j++ {
|
||||
sm.SendEvent(EventStart)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// If we reach here without panicking, the test passes
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build embed
|
||||
// +build embed
|
||||
|
||||
package cocosai
|
||||
|
||||
import _ "embed"
|
||||
|
||||
+267
-724
File diff suppressed because it is too large
Load Diff
+278
-15
@@ -3,15 +3,26 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/azure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/gcp"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type fieldType int
|
||||
@@ -30,13 +41,16 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errDecode = errors.New("base64 string could not be decoded")
|
||||
errDataLength = errors.New("data does not have an adequate length")
|
||||
errReadingAttestationPolicyFile = errors.New("error while reading the attestation policy file")
|
||||
errUnmarshalJSON = errors.New("failed to unmarshal json")
|
||||
errMarshalJSON = errors.New("failed to marshal json")
|
||||
errWriteFile = errors.New("failed to write to file")
|
||||
errAttestationPolicyField = errors.New("the specified field type does not exist in the attestation policy")
|
||||
errDecode = errors.New("base64 string could not be decoded")
|
||||
errDataLength = errors.New("data does not have an adequate length")
|
||||
errReadingAttestationPolicyFile = errors.New("error while reading the attestation policy file")
|
||||
errUnmarshalJSON = errors.New("failed to unmarshal json")
|
||||
errMarshalJSON = errors.New("failed to marshal json")
|
||||
errWriteFile = errors.New("failed to write to file")
|
||||
errAttestationPolicyField = errors.New("the specified field type does not exist in the attestation policy")
|
||||
errReadingManifestFile = errors.New("error while reading manifest file")
|
||||
errDecodeHex = errors.New("error decoding hex string")
|
||||
policy uint64 = 196639
|
||||
)
|
||||
|
||||
func (cli *CLI) NewAttestationPolicyCmd() *cobra.Command {
|
||||
@@ -99,6 +113,189 @@ func (cli *CLI) NewAddHostDataCmd() *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *CLI) NewGCPAttestationPolicy() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "gcp",
|
||||
Short: "Get attestation policy for GCP CVM",
|
||||
Example: `gcp <bin_vtmp_attestation_report_file> <vcpu_count>`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
attestationBin, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
printError(cmd, "Error reading attestation report file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
vcpuCount, err := strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
printError(cmd, "Error converting vCPU count to integer: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestation := &attest.Attestation{}
|
||||
|
||||
if err := proto.Unmarshal(attestationBin, attestation); err != nil {
|
||||
printError(cmd, "Error unmarshaling attestation report: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestationPB := attestation.GetSevSnpAttestation()
|
||||
|
||||
measurement, err := gcp.Extract384BitMeasurement(attestationPB)
|
||||
if err != nil {
|
||||
printError(cmd, "Error extracting 384-bit measurement: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
launchEndorsement, err := gcp.GetLaunchEndorsement(cmd.Context(), measurement)
|
||||
if err != nil {
|
||||
printError(cmd, "Error getting launch endorsement: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestationPolicy, err := gcp.GenerateAttestationPolicy(launchEndorsement, uint32(vcpuCount))
|
||||
if err != nil {
|
||||
printError(cmd, "Error generating attestation policy: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestationPolicyJson, err := json.MarshalIndent(attestationPolicy, "", " ")
|
||||
if err != nil {
|
||||
printError(cmd, "Error marshaling attestation policy: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile("attestation_policy.json", attestationPolicyJson, filePermission); err != nil {
|
||||
printError(cmd, "Error writing attestation policy file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println("Attestation policy file generated successfully ✅")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *CLI) NewDownloadGCPOvmfFile() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "download",
|
||||
Short: "Download GCP OVMF file",
|
||||
Example: `download <bin_vtmp_attestation_report_file>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
attestationBin, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
printError(cmd, "Error reading attestation report file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestation := &attest.Attestation{}
|
||||
|
||||
if err := proto.Unmarshal(attestationBin, attestation); err != nil {
|
||||
printError(cmd, "Error unmarshaling attestation report: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestationPB := attestation.GetSevSnpAttestation()
|
||||
|
||||
measurement, err := gcp.Extract384BitMeasurement(attestationPB)
|
||||
if err != nil {
|
||||
printError(cmd, "Error extracting 384-bit measurement: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
launchEndorsement, err := gcp.GetLaunchEndorsement(cmd.Context(), measurement)
|
||||
if err != nil {
|
||||
printError(cmd, "Error getting launch endorsement: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
ovmf, err := gcp.DownloadOvmfFile(cmd.Context(), fmt.Sprintf("%x", launchEndorsement.Digest))
|
||||
if err != nil {
|
||||
printError(cmd, "Error downloading OVMF file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
sum384 := sha512.Sum384(ovmf)
|
||||
|
||||
if !bytes.Equal(sum384[:], launchEndorsement.Digest) {
|
||||
printError(cmd, "Error OVMF file does not match the measurement: %v ❌ ", fmt.Errorf("digest mismatch"))
|
||||
} else {
|
||||
cmd.Println("OVMF firmware in vm is unmodified ✅")
|
||||
}
|
||||
|
||||
if err := os.WriteFile("ovmf.fd", ovmf, filePermission); err != nil {
|
||||
printError(cmd, "Error writing OVMF file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println("OVMF file downloaded successfully ✅")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *CLI) NewAzureAttestationPolicy() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "azure",
|
||||
Short: "Get attestation policy for Azure CVM",
|
||||
Example: `azure <azure_maa_token_file> <product_name>`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
token, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
printError(cmd, "Error reading attestation report file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
product := args[1]
|
||||
|
||||
config, err := azure.GenerateAttestationPolicy(string(token), product, policy)
|
||||
if err != nil {
|
||||
printError(cmd, "Error generating attestation policy: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
attestationPolicyJson, err := json.MarshalIndent(&config, "", " ")
|
||||
if err != nil {
|
||||
printError(cmd, "Error marshaling attestation policy: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile("attestation_policy.json", attestationPolicyJson, filePermission); err != nil {
|
||||
printError(cmd, "Error writing attestation policy file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println("Attestation policy file generated successfully ✅")
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Uint64Var(
|
||||
&policy,
|
||||
"policy",
|
||||
policy,
|
||||
"Policy of the guest CVM",
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli *CLI) NewExtendWithManifestCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "extend",
|
||||
Short: "Extends PCR16 with computation manifests. The first parameter is path to attestation policy file. The rest of the parameters are paths to computation manifest files.",
|
||||
Example: "extend <attestation_policy_file_path> <computation_manifest_file_path> [<computation_manifest_file_path> ...]",
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
attestationPolicyFilePath := args[0]
|
||||
manifestPaths := args[1:]
|
||||
if err := extendWithManifest(attestationPolicyFilePath, manifestPaths); err != nil {
|
||||
printError(cmd, "Error could not extend PCR16: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func changeAttestationConfiguration(fileName, base64Data string, expectedLength int, field fieldType) error {
|
||||
data, err := base64.StdEncoding.DecodeString(base64Data)
|
||||
if err != nil {
|
||||
@@ -109,27 +306,31 @@ func changeAttestationConfiguration(fileName, base64Data string, expectedLength
|
||||
return errDataLength
|
||||
}
|
||||
|
||||
ac := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
ac := attestation.Config{Config: &check.Config{RootOfTrust: &check.RootOfTrust{}, Policy: &check.Policy{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
|
||||
attestationPolicy, err := os.ReadFile(fileName)
|
||||
f, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return errors.Wrap(errReadingAttestationPolicyFile, err)
|
||||
}
|
||||
|
||||
if err = protojson.Unmarshal(attestationPolicy, &ac); err != nil {
|
||||
if err = vtpm.ReadPolicyFromByte(f, &ac); err != nil {
|
||||
return errors.Wrap(errUnmarshalJSON, err)
|
||||
}
|
||||
|
||||
if ac.Config.Policy == nil {
|
||||
ac.Config.Policy = &check.Policy{}
|
||||
}
|
||||
|
||||
switch field {
|
||||
case measurementField:
|
||||
ac.Policy.Measurement = data
|
||||
ac.Config.Policy.Measurement = data
|
||||
case hostDataField:
|
||||
ac.Policy.HostData = data
|
||||
ac.Config.Policy.HostData = data
|
||||
default:
|
||||
return errAttestationPolicyField
|
||||
}
|
||||
|
||||
fileJson, err := protojson.Marshal(&ac)
|
||||
fileJson, err := vtpm.ConvertPolicyToJSON(&ac)
|
||||
if err != nil {
|
||||
return errors.Wrap(errMarshalJSON, err)
|
||||
}
|
||||
@@ -138,3 +339,65 @@ func changeAttestationConfiguration(fileName, base64Data string, expectedLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extendWithManifest(attestationPolicyPath string, manifestPaths []string) error {
|
||||
attestationConfig := attestation.Config{Config: &check.Config{RootOfTrust: &check.RootOfTrust{}, Policy: &check.Policy{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
|
||||
attestationPolicyFileData, err := os.ReadFile(attestationPolicyPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(errReadingAttestationPolicyFile, err)
|
||||
}
|
||||
|
||||
if err = vtpm.ReadPolicyFromByte(attestationPolicyFileData, &attestationConfig); err != nil {
|
||||
return errors.Wrap(errUnmarshalJSON, err)
|
||||
}
|
||||
|
||||
for _, manifestPath := range manifestPaths {
|
||||
manifest, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(errReadingManifestFile, err)
|
||||
}
|
||||
|
||||
manifestSha256 := sha512.Sum512_256(manifest)
|
||||
manifestSha384 := sha512.Sum384(manifest)
|
||||
|
||||
data256, exists256 := attestationConfig.PCRValues.Sha256["16"]
|
||||
|
||||
if !exists256 {
|
||||
data256 = strings.Repeat("0", 64) // 32 bytes in hex
|
||||
}
|
||||
|
||||
byteData256, err := hex.DecodeString(data256)
|
||||
if err != nil {
|
||||
return errors.Wrap(errDecodeHex, err)
|
||||
}
|
||||
|
||||
newByteData256 := sha512.Sum512_256(append(byteData256, manifestSha256[:]...))
|
||||
|
||||
data384, exists384 := attestationConfig.PCRValues.Sha384["16"]
|
||||
|
||||
if !exists384 {
|
||||
data384 = strings.Repeat("0", 96) // 48 bytes in hex
|
||||
}
|
||||
|
||||
byteData384, err := hex.DecodeString(data384)
|
||||
if err != nil {
|
||||
return errors.Wrap(errDecodeHex, err)
|
||||
}
|
||||
|
||||
newByteData384 := sha512.Sum384(append(byteData384, manifestSha384[:]...))
|
||||
|
||||
attestationConfig.PCRValues.Sha256["16"] = hex.EncodeToString(newByteData256[:])
|
||||
attestationConfig.PCRValues.Sha384["16"] = hex.EncodeToString(newByteData384[:])
|
||||
}
|
||||
|
||||
attestationPolicyJSON, err := vtpm.ConvertPolicyToJSON(&attestationConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(errMarshalJSON, err)
|
||||
}
|
||||
if err = os.WriteFile(attestationPolicyPath, attestationPolicyJSON, filePermission); err != nil {
|
||||
return errors.Wrap(errWriteFile, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
func TestChangeAttestationConfiguration(t *testing.T) {
|
||||
@@ -18,9 +21,9 @@ func TestChangeAttestationConfiguration(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
initialConfig := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
initialConfig := attestation.Config{Config: &check.Config{RootOfTrust: &check.RootOfTrust{}, Policy: &check.Policy{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
|
||||
initialJSON, err := protojson.Marshal(&initialConfig)
|
||||
initialJSON, err := json.Marshal(initialConfig)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(tmpfile.Name(), initialJSON, 0o644)
|
||||
require.NoError(t, err)
|
||||
@@ -86,15 +89,15 @@ func TestChangeAttestationConfiguration(t *testing.T) {
|
||||
content, err := os.ReadFile(tmpfile.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
config := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
err = protojson.Unmarshal(content, &config)
|
||||
ap := attestation.Config{Config: &check.Config{RootOfTrust: &check.RootOfTrust{}, Policy: &check.Policy{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
err = vtpm.ReadPolicyFromByte(content, &ap)
|
||||
require.NoError(t, err)
|
||||
|
||||
decodedData, _ := base64.StdEncoding.DecodeString(tt.base64Data)
|
||||
if tt.field == measurementField {
|
||||
assert.Equal(t, decodedData, config.Policy.Measurement)
|
||||
assert.Equal(t, decodedData, ap.Config.Policy.Measurement)
|
||||
} else if tt.field == hostDataField {
|
||||
assert.Equal(t, decodedData, config.Policy.HostData)
|
||||
assert.Equal(t, decodedData, ap.Config.Policy.HostData)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -129,3 +132,340 @@ func TestNewAddHostDataCmd(t *testing.T) {
|
||||
assert.Equal(t, "hostdata <host-data> <attestation_policy.json>", cmd.Example)
|
||||
assert.NotNil(t, cmd.Run)
|
||||
}
|
||||
|
||||
func TestChangeAttestationConfigurationFileErrors(t *testing.T) {
|
||||
t.Run("File Not Found", func(t *testing.T) {
|
||||
err := changeAttestationConfiguration("nonexistent.json", base64.StdEncoding.EncodeToString(make([]byte, measurementLength)), measurementLength, measurementField)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "error while reading the attestation policy file")
|
||||
})
|
||||
|
||||
t.Run("Invalid JSON Content", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "invalid.json")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("invalid json"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = changeAttestationConfiguration(tmpfile.Name(), base64.StdEncoding.EncodeToString(make([]byte, measurementLength)), measurementLength, measurementField)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to unmarshal json")
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewGCPAttestationPolicy(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
cmd := cli.NewGCPAttestationPolicy()
|
||||
|
||||
assert.Equal(t, "gcp", cmd.Use)
|
||||
assert.Equal(t, "Get attestation policy for GCP CVM", cmd.Short)
|
||||
assert.Equal(t, "gcp <bin_vtmp_attestation_report_file> <vcpu_count>", cmd.Example)
|
||||
assert.NotNil(t, cmd.Run)
|
||||
|
||||
t.Run("File Not Found", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"nonexistent.bin", "4"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error reading attestation report file")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Invalid vCPU Count", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "attestation.bin")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("dummy content"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd.SetArgs([]string{tmpfile.Name(), "invalid"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error converting vCPU count to integer")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Invalid Attestation Data", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "attestation.bin")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("invalid protobuf data"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd.SetArgs([]string{tmpfile.Name(), "4"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error unmarshaling attestation report")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewDownloadGCPOvmfFile(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
cmd := cli.NewDownloadGCPOvmfFile()
|
||||
|
||||
assert.Equal(t, "download", cmd.Use)
|
||||
assert.Equal(t, "Download GCP OVMF file", cmd.Short)
|
||||
assert.Equal(t, "download <bin_vtmp_attestation_report_file>", cmd.Example)
|
||||
assert.NotNil(t, cmd.Run)
|
||||
|
||||
t.Run("File Not Found", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"nonexistent.bin"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error reading attestation report file")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Invalid Attestation Data", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "attestation.bin")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("invalid protobuf data"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd.SetArgs([]string{tmpfile.Name()})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error unmarshaling attestation report")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewAzureAttestationPolicy(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
cmd := cli.NewAzureAttestationPolicy()
|
||||
|
||||
assert.Equal(t, "azure", cmd.Use)
|
||||
assert.Equal(t, "Get attestation policy for Azure CVM", cmd.Short)
|
||||
assert.Equal(t, "azure <azure_maa_token_file> <product_name>", cmd.Example)
|
||||
assert.NotNil(t, cmd.Run)
|
||||
|
||||
flag := cmd.Flags().Lookup("policy")
|
||||
assert.NotNil(t, flag)
|
||||
assert.Equal(t, "Policy of the guest CVM", flag.Usage)
|
||||
|
||||
t.Run("File Not Found", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"nonexistent.token", "test-product"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error reading attestation report file")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Valid Token File", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "token.maa")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("dummy.token.content"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer os.Remove("attestation_policy.json")
|
||||
|
||||
cmd.SetArgs([]string{tmpfile.Name(), "test-product"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Custom Policy Flag", func(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "token.maa")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
err = os.WriteFile(tmpfile.Name(), []byte("dummy.token.content"), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd.SetArgs([]string{"--policy", "123456", tmpfile.Name(), "test-product"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
flag := cmd.Flags().Lookup("policy")
|
||||
assert.NotNil(t, flag)
|
||||
assert.Equal(t, "123456", flag.Value.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestCommandErrorHandling(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
|
||||
t.Run("Measurement Command Error", func(t *testing.T) {
|
||||
cmd := cli.NewAddMeasurementCmd()
|
||||
cmd.SetArgs([]string{"invalid-base64", "nonexistent.json"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error could not change measurement data")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Host Data Command Error", func(t *testing.T) {
|
||||
cmd := cli.NewAddHostDataCmd()
|
||||
cmd.SetArgs([]string{"invalid-base64", "nonexistent.json"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "Error could not change host data")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
}
|
||||
|
||||
func TestExtendWithManifestHandling(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
|
||||
t.Run("Invalid policy file", func(t *testing.T) {
|
||||
cmd := cli.NewExtendWithManifestCmd()
|
||||
cmd.SetArgs([]string{"nonexistent.policy.json", "nonexistent.manifest.json"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "error while reading the attestation policy file")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Invalid manifest file", func(t *testing.T) {
|
||||
cmd := cli.NewExtendWithManifestCmd()
|
||||
cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", "nonexistent.manifest.json"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "error while reading manifest file")
|
||||
assert.Contains(t, output, "❌")
|
||||
})
|
||||
|
||||
t.Run("Valid file paths", func(t *testing.T) {
|
||||
fileContent := `{
|
||||
"id": "1",
|
||||
"name": "sample computation",
|
||||
"description": "sample description",
|
||||
"datasets": [
|
||||
{
|
||||
"hash": "<sha3_encoded string>",
|
||||
"userKey": "<pem_encoded public key string>"
|
||||
}
|
||||
],
|
||||
"algorithm": {
|
||||
"hash": "<sha3_encoded string>",
|
||||
"userKey": "<pem_encoded public key string>"
|
||||
},
|
||||
"result_consumers": [
|
||||
{
|
||||
"userKey": "<pem_encoded public key string>"
|
||||
}
|
||||
],
|
||||
"agent_config": {
|
||||
"port": "7002",
|
||||
"cert_file": "<pem encoded cert string>",
|
||||
"key_file": "<pem encoded private key string>",
|
||||
"server_ca_file": "<pem encoded cert string>",
|
||||
"client_ca_file": "<pem encoded cert string>",
|
||||
"attested_tls": true
|
||||
}
|
||||
}`
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting current working directory: %v", err)
|
||||
}
|
||||
|
||||
manifestFile, err := os.CreateTemp(dir, "manifest.json")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(manifestFile.Name())
|
||||
|
||||
err = os.WriteFile(manifestFile.Name(), []byte(fileContent), 0o644)
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing temp file: %v", err)
|
||||
}
|
||||
|
||||
cmd := cli.NewExtendWithManifestCmd()
|
||||
cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", manifestFile.Name()})
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,597 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
tpmAttest "github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
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"
|
||||
vtpmFilePath = "../quote.dat"
|
||||
attestationReportJson = "attestation.json"
|
||||
sevSnpProductMilan = "Milan"
|
||||
sevSnpProductGenoa = "Genoa"
|
||||
FormatBinaryPB = "binarypb"
|
||||
FormatTextProto = "textproto"
|
||||
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":1,
|
||||
"stepping":1,
|
||||
"machineStepping":1
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
var cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
|
||||
func addSEVSNPVerificationOptions(cmd *cobra.Command) *cobra.Command {
|
||||
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.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(
|
||||
&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.ProductLine,
|
||||
"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.",
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func validateInput() error {
|
||||
if len(cfg.RootOfTrust.CabundlePaths) != 0 || len(cfg.RootOfTrust.Cabundles) != 0 && cfg.RootOfTrust.ProductLine == "" {
|
||||
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 parseTrustedKeys() error {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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":"1",
|
||||
"stepping":1,
|
||||
"machineStepping":1
|
||||
}
|
||||
}
|
||||
}*/
|
||||
func parseConfig() error {
|
||||
if cfgString == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
policyByte, err := os.ReadFile(cfgString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := protojson.Unmarshal(policyByte, &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 parseAttestationFile() error {
|
||||
file, err := os.ReadFile(attestationFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attestationRaw = file
|
||||
if isFileJSON(attestationFile) {
|
||||
attestationRaw, err = attesationFromJSON(attestationRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sevsnpverify(cmd *cobra.Command, verifier attestation.Verifier, args []string) error {
|
||||
cmd.Println("Checking attestation")
|
||||
|
||||
attestationFile = string(args[0])
|
||||
|
||||
if err := parseAttestationFile(); err != nil {
|
||||
return fmt.Errorf("error parsing config: %v ❌ ", err)
|
||||
}
|
||||
|
||||
// This format is the attestation report in AMD's specified ABI format, immediately
|
||||
// followed by the certificate table bytes.
|
||||
if len(attestationRaw) < abi.ReportSize {
|
||||
return fmt.Errorf("attestation too small: got 0x%x bytes, need at least 0x%x bytes", len(attestationRaw), abi.ReportSize)
|
||||
}
|
||||
|
||||
if err := parseAttestationConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := verifier.VerifTeeAttestation(attestationRaw, cfg.Policy.ReportData); err != nil {
|
||||
return fmt.Errorf("attestation validation and verification failed with error: %v ❌ ", err)
|
||||
}
|
||||
|
||||
cmd.Println("Attestation validation and verification is successful!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseAttestationConfig() error {
|
||||
if err := parseConfig(); err != nil {
|
||||
return fmt.Errorf("error parsing config: %v ❌ ", err)
|
||||
}
|
||||
if err := parseHashes(); err != nil {
|
||||
return fmt.Errorf("error parsing hashes: %v ❌ ", err)
|
||||
}
|
||||
if err := parseTrustedKeys(); err != nil {
|
||||
return fmt.Errorf("error parsing files: %v ❌ ", err)
|
||||
}
|
||||
|
||||
if err := parseUints(); err != nil {
|
||||
return fmt.Errorf("error parsing uints: %v ❌ ", err)
|
||||
}
|
||||
|
||||
if err := validateInput(); err != nil {
|
||||
return fmt.Errorf("error validating input: %v ❌ ", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func vtpmSevSnpverify(args []string, verifier attestation.Verifier) error {
|
||||
attest, err := returnvTPMAttestation(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := parseAttestationConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := verifier.VerifyAttestation(attest, cfg.Policy.ReportData, nonce); err != nil {
|
||||
return fmt.Errorf("attestation validation and verification failed with error: %v ❌ ", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func vtpmverify(args []string, verifier attestation.Verifier) error {
|
||||
attestation, err := returnvTPMAttestation(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := verifier.VerifVTpmAttestation(attestation, nonce); err != nil {
|
||||
return fmt.Errorf("attestation validation and verification failed with error: %v ❌ ", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func returnvTPMAttestation(args []string) ([]byte, error) {
|
||||
attestationFile = string(args[0])
|
||||
input, err := openInputFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if closer, ok := input.(*os.File); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
attestationBytes, err := io.ReadAll(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attestation := &tpmAttest.Attestation{}
|
||||
|
||||
if format == FormatBinaryPB {
|
||||
return attestationBytes, nil
|
||||
} else if format == FormatTextProto {
|
||||
unmarshalOptions := prototext.UnmarshalOptions{}
|
||||
err = unmarshalOptions.Unmarshal(attestationBytes, attestation)
|
||||
} else {
|
||||
return nil, fmt.Errorf("format should be either binarypb or textproto")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to unmarshal attestation report: %v", err)
|
||||
}
|
||||
|
||||
attestationBytes, err = proto.Marshal(attestation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to marshal vTPM attestation report: %v", err)
|
||||
}
|
||||
|
||||
return attestationBytes, nil
|
||||
}
|
||||
@@ -0,0 +1,870 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/proto/sevsnp"
|
||||
tpmAttest "github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/google/go-tpm-tools/proto/tpm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/mocks"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestAddSEVSNPVerificationOptions(t *testing.T) {
|
||||
cmd := &cobra.Command{
|
||||
Use: "test",
|
||||
}
|
||||
|
||||
result := addSEVSNPVerificationOptions(cmd)
|
||||
|
||||
assert.Equal(t, cmd, result)
|
||||
|
||||
// Check that important flags are added
|
||||
flags := []string{
|
||||
"host_data",
|
||||
"family_id",
|
||||
"image_id",
|
||||
"report_id",
|
||||
"report_id_ma",
|
||||
"measurement",
|
||||
"chip_id",
|
||||
"minimum_tcb",
|
||||
"minimum_lauch_tcb",
|
||||
"guest_policy",
|
||||
"minimum_guest_svn",
|
||||
"minimum_build",
|
||||
"check_crl",
|
||||
"timeout",
|
||||
"max_retry_delay",
|
||||
"require_author_key",
|
||||
"require_id_block",
|
||||
"platform_info",
|
||||
"minimum_version",
|
||||
"trusted_author_keys",
|
||||
"trusted_author_key_hashes",
|
||||
"trusted_id_keys",
|
||||
"trusted_id_key_hashes",
|
||||
"product",
|
||||
"stepping",
|
||||
"CA_bundles_paths",
|
||||
"CA_bundles",
|
||||
}
|
||||
|
||||
for _, flagName := range flags {
|
||||
flag := cmd.Flags().Lookup(flagName)
|
||||
assert.NotNil(t, flag, "Flag %s should exist", flagName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateInput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupCfg func()
|
||||
expectErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid empty config",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "CA bundles without product name",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{},
|
||||
RootOfTrust: &check.RootOfTrust{
|
||||
CabundlePaths: []string{"test.pem"},
|
||||
ProductLine: "",
|
||||
},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "product name must be set if CA bundles are provided",
|
||||
},
|
||||
{
|
||||
name: "invalid report_data length",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
ReportData: []byte("invalid"),
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "report_data",
|
||||
},
|
||||
{
|
||||
name: "invalid host_data length",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
HostData: []byte("invalid"),
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "host_data",
|
||||
},
|
||||
{
|
||||
name: "invalid family_id length",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
FamilyId: []byte("invalid"),
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "family_id",
|
||||
},
|
||||
{
|
||||
name: "invalid image_id length",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
ImageId: []byte("invalid"),
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "image_id",
|
||||
},
|
||||
{
|
||||
name: "invalid trusted author key hash",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
TrustedAuthorKeyHashes: [][]byte{[]byte("invalid")},
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "trusted_author_key_hash",
|
||||
},
|
||||
{
|
||||
name: "invalid trusted id key hash",
|
||||
setupCfg: func() {
|
||||
cfg = check.Config{
|
||||
Policy: &check.Policy{
|
||||
TrustedIdKeyHashes: [][]byte{[]byte("invalid")},
|
||||
},
|
||||
RootOfTrust: &check.RootOfTrust{},
|
||||
}
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "trusted_id_key_hash",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.setupCfg()
|
||||
err := validateInput()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.errMsg)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrustedKeys(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
authorKeyFile := filepath.Join(tempDir, "author.pem")
|
||||
idKeyFile := filepath.Join(tempDir, "id.pem")
|
||||
nonExistentFile := filepath.Join(tempDir, "nonexistent.pem")
|
||||
|
||||
authorKeyContent := "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAOI..."
|
||||
idKeyContent := "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAOI..."
|
||||
|
||||
require.NoError(t, os.WriteFile(authorKeyFile, []byte(authorKeyContent), 0o644))
|
||||
require.NoError(t, os.WriteFile(idKeyFile, []byte(idKeyContent), 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
trustedAuthorKeys []string
|
||||
trustedIdKeys []string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid files",
|
||||
trustedAuthorKeys: []string{authorKeyFile},
|
||||
trustedIdKeys: []string{idKeyFile},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "nonexistent author key file",
|
||||
trustedAuthorKeys: []string{nonExistentFile},
|
||||
trustedIdKeys: []string{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "nonexistent id key file",
|
||||
trustedAuthorKeys: []string{},
|
||||
trustedIdKeys: []string{nonExistentFile},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty file lists",
|
||||
trustedAuthorKeys: []string{},
|
||||
trustedIdKeys: []string{},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
trustedAuthorKeys = tt.trustedAuthorKeys
|
||||
trustedIdKeys = tt.trustedIdKeys
|
||||
|
||||
err := parseTrustedKeys()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if len(tt.trustedAuthorKeys) > 0 {
|
||||
assert.Len(t, cfg.Policy.TrustedAuthorKeys, len(tt.trustedAuthorKeys))
|
||||
assert.Equal(t, []byte(authorKeyContent), cfg.Policy.TrustedAuthorKeys[0])
|
||||
}
|
||||
if len(tt.trustedIdKeys) > 0 {
|
||||
assert.Len(t, cfg.Policy.TrustedIdKeys, len(tt.trustedIdKeys))
|
||||
assert.Equal(t, []byte(idKeyContent), cfg.Policy.TrustedIdKeys[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseUints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stepping string
|
||||
platformInfo string
|
||||
expectErr bool
|
||||
expectedStep *uint32
|
||||
expectedPlatform *uint64
|
||||
}{
|
||||
{
|
||||
name: "empty values",
|
||||
stepping: "",
|
||||
platformInfo: "",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "decimal values",
|
||||
stepping: "5",
|
||||
platformInfo: "10",
|
||||
expectErr: false,
|
||||
expectedStep: uint32Ptr(5),
|
||||
expectedPlatform: uint64Ptr(10),
|
||||
},
|
||||
{
|
||||
name: "hex values",
|
||||
stepping: "0x5",
|
||||
platformInfo: "0xa",
|
||||
expectErr: false,
|
||||
expectedStep: uint32Ptr(5),
|
||||
expectedPlatform: uint64Ptr(10),
|
||||
},
|
||||
{
|
||||
name: "octal values",
|
||||
stepping: "0o7",
|
||||
platformInfo: "0o12",
|
||||
expectErr: false,
|
||||
expectedStep: uint32Ptr(7),
|
||||
expectedPlatform: uint64Ptr(10),
|
||||
},
|
||||
{
|
||||
name: "binary values",
|
||||
stepping: "0b101",
|
||||
platformInfo: "0b1010",
|
||||
expectErr: false,
|
||||
expectedStep: uint32Ptr(5),
|
||||
expectedPlatform: uint64Ptr(10),
|
||||
},
|
||||
{
|
||||
name: "invalid stepping",
|
||||
stepping: "invalid",
|
||||
platformInfo: "",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid platform info",
|
||||
stepping: "",
|
||||
platformInfo: "invalid",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg = check.Config{Policy: &check.Policy{Product: &sevsnp.SevProduct{}}, RootOfTrust: &check.RootOfTrust{}}
|
||||
stepping = tt.stepping
|
||||
platformInfo = tt.platformInfo
|
||||
|
||||
err := parseUints()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tt.expectedStep != nil {
|
||||
assert.Equal(t, *tt.expectedStep, cfg.Policy.Product.MachineStepping.Value)
|
||||
}
|
||||
if tt.expectedPlatform != nil {
|
||||
assert.Equal(t, *tt.expectedPlatform, cfg.Policy.PlatformInfo.Value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBase(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected int
|
||||
}{
|
||||
{"0x10", 16},
|
||||
{"0o10", 8},
|
||||
{"0b10", 2},
|
||||
{"10", 10},
|
||||
{"", 10},
|
||||
{"abc", 10},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
result := getBase(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConfig(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
validConfig := map[string]interface{}{
|
||||
"rootOfTrust": map[string]interface{}{
|
||||
"product": "test_product",
|
||||
"cabundlePaths": []string{"test_path"},
|
||||
"cabundles": []string{"test_bundle"},
|
||||
"checkCrl": true,
|
||||
"disallowNetwork": true,
|
||||
},
|
||||
"policy": map[string]interface{}{
|
||||
"minimumGuestSvn": 1,
|
||||
"policy": "1",
|
||||
"minimumBuild": 1,
|
||||
"minimumVersion": "0.90",
|
||||
"requireAuthorKey": true,
|
||||
"requireIdBlock": true,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setupConfig func() string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty config string",
|
||||
setupConfig: func() string {
|
||||
return ""
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid config file",
|
||||
setupConfig: func() string {
|
||||
configFile := filepath.Join(tempDir, "valid_config.json")
|
||||
configBytes, err := json.Marshal(validConfig)
|
||||
assert.NoError(t, err)
|
||||
if err := os.WriteFile(configFile, configBytes, 0o644); err != nil {
|
||||
t.Errorf("failed to write config file: %v", err)
|
||||
}
|
||||
return configFile
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "nonexistent config file",
|
||||
setupConfig: func() string {
|
||||
return filepath.Join(tempDir, "nonexistent.json")
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid JSON config",
|
||||
setupConfig: func() string {
|
||||
configFile := filepath.Join(tempDir, "invalid_config.json")
|
||||
if err := os.WriteFile(configFile, []byte("invalid json"), 0o644); err != nil {
|
||||
t.Errorf("failed to write invalid config file: %v", err)
|
||||
}
|
||||
return configFile
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
cfgString = tt.setupConfig()
|
||||
|
||||
err := parseConfig()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, cfg.Policy)
|
||||
assert.NotNil(t, cfg.RootOfTrust)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHashes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
trustedAuthorHashes []string
|
||||
trustedIdKeyHashes []string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid hashes",
|
||||
trustedAuthorHashes: []string{"deadbeef", "cafebabe"},
|
||||
trustedIdKeyHashes: []string{"12345678", "87654321"},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty hashes",
|
||||
trustedAuthorHashes: []string{},
|
||||
trustedIdKeyHashes: []string{},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid author hash",
|
||||
trustedAuthorHashes: []string{"invalid_hex"},
|
||||
trustedIdKeyHashes: []string{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid id key hash",
|
||||
trustedAuthorHashes: []string{},
|
||||
trustedIdKeyHashes: []string{"invalid_hex"},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
trustedAuthorHashes = tt.trustedAuthorHashes
|
||||
trustedIdKeyHashes = tt.trustedIdKeyHashes
|
||||
|
||||
err := parseHashes()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, cfg.Policy.TrustedAuthorKeyHashes, len(tt.trustedAuthorHashes))
|
||||
assert.Len(t, cfg.Policy.TrustedIdKeyHashes, len(tt.trustedIdKeyHashes))
|
||||
|
||||
for i, hash := range tt.trustedAuthorHashes {
|
||||
expected, _ := hex.DecodeString(hash)
|
||||
assert.Equal(t, expected, cfg.Policy.TrustedAuthorKeyHashes[i])
|
||||
}
|
||||
|
||||
for i, hash := range tt.trustedIdKeyHashes {
|
||||
expected, _ := hex.DecodeString(hash)
|
||||
assert.Equal(t, expected, cfg.Policy.TrustedIdKeyHashes[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAttestationFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
binaryFile := filepath.Join(tempDir, "attestation.bin")
|
||||
jsonFile := filepath.Join(tempDir, "attestation.json")
|
||||
|
||||
binaryData := make([]byte, 1024)
|
||||
for i := range binaryData {
|
||||
binaryData[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
jsonData := &sevsnp.Attestation{
|
||||
Report: &sevsnp.Report{
|
||||
FamilyId: make([]byte, 16),
|
||||
ImageId: make([]byte, 16),
|
||||
ReportData: make([]byte, 64),
|
||||
Measurement: make([]byte, 48),
|
||||
HostData: make([]byte, 32),
|
||||
IdKeyDigest: make([]byte, 48),
|
||||
AuthorKeyDigest: make([]byte, 48),
|
||||
ReportId: make([]byte, 32),
|
||||
ReportIdMa: make([]byte, 32),
|
||||
ChipId: make([]byte, 64),
|
||||
Signature: make([]byte, 512),
|
||||
},
|
||||
}
|
||||
jsonBytes, err := json.Marshal(jsonData)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, os.WriteFile(binaryFile, binaryData, 0o644))
|
||||
require.NoError(t, os.WriteFile(jsonFile, jsonBytes, 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationFile string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid binary file",
|
||||
attestationFile: binaryFile,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid JSON file",
|
||||
attestationFile: jsonFile,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "nonexistent file",
|
||||
attestationFile: filepath.Join(tempDir, "nonexistent.bin"),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
attestationFile = tt.attestationFile
|
||||
|
||||
err := parseAttestationFile()
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, attestationRaw)
|
||||
assert.NotEmpty(t, attestationRaw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSevsnpverify(t *testing.T) {
|
||||
trustedAuthorHashes = []string{}
|
||||
trustedIdKeyHashes = []string{}
|
||||
stepping = ""
|
||||
platformInfo = ""
|
||||
tempDir := t.TempDir()
|
||||
cfg = check.Config{Policy: &check.Policy{Product: &sevsnp.SevProduct{}}, RootOfTrust: &check.RootOfTrust{}}
|
||||
|
||||
attestationFile := filepath.Join(tempDir, "attestation.bin")
|
||||
attestationData := make([]byte, abi.ReportSize+100)
|
||||
for i := range attestationData {
|
||||
attestationData[i] = byte(i % 256)
|
||||
}
|
||||
require.NoError(t, os.WriteFile(attestationFile, attestationData, 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
setupMock func(*mocks.Verifier)
|
||||
expectErr bool
|
||||
expectedMsg string
|
||||
}{
|
||||
{
|
||||
name: "successful verification",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifTeeAttestation", mock.Anything, mock.Anything).Return(nil)
|
||||
},
|
||||
expectErr: false,
|
||||
expectedMsg: "Attestation validation and verification is successful!",
|
||||
},
|
||||
{
|
||||
name: "verification failure",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifTeeAttestation", mock.Anything, mock.Anything).Return(fmt.Errorf("verification failed"))
|
||||
},
|
||||
expectErr: true,
|
||||
expectedMsg: "attestation validation and verification failed",
|
||||
},
|
||||
{
|
||||
name: "nonexistent file",
|
||||
args: []string{filepath.Join(tempDir, "nonexistent.bin")},
|
||||
setupMock: func(m *mocks.Verifier) {},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfgString = ""
|
||||
|
||||
mockVerifier := new(mocks.Verifier)
|
||||
tt.setupMock(mockVerifier)
|
||||
|
||||
var output bytes.Buffer
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetOut(&output)
|
||||
|
||||
err := sevsnpverify(cmd, mockVerifier, tt.args)
|
||||
fmt.Println("error1", err)
|
||||
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tt.expectedMsg != "" {
|
||||
assert.Contains(t, output.String(), tt.expectedMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mockVerifier.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReturnvTPMAttestation(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
attestation := &tpmAttest.Attestation{
|
||||
Quotes: []*tpm.Quote{
|
||||
{
|
||||
Quote: []byte("test quote"),
|
||||
RawSig: []byte("test signature"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
binaryData, err := proto.Marshal(attestation)
|
||||
require.NoError(t, err)
|
||||
|
||||
binaryFile := filepath.Join(tempDir, "attestation.pb")
|
||||
require.NoError(t, os.WriteFile(binaryFile, binaryData, 0o644))
|
||||
|
||||
textData, err := prototext.Marshal(attestation)
|
||||
require.NoError(t, err)
|
||||
|
||||
textFile := filepath.Join(tempDir, "attestation.txtpb")
|
||||
require.NoError(t, os.WriteFile(textFile, textData, 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
format string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "binary protobuf format",
|
||||
args: []string{binaryFile},
|
||||
format: FormatBinaryPB,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "text protobuf format",
|
||||
args: []string{textFile},
|
||||
format: FormatTextProto,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid format",
|
||||
args: []string{binaryFile},
|
||||
format: "invalid",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "nonexistent file",
|
||||
args: []string{filepath.Join(tempDir, "nonexistent.pb")},
|
||||
format: FormatBinaryPB,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
format = tt.format
|
||||
|
||||
result, err := returnvTPMAttestation(tt.args)
|
||||
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.NotEmpty(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVtpmSevSnpverify(t *testing.T) {
|
||||
stepping = ""
|
||||
platformInfo = ""
|
||||
trustedAuthorHashes = []string{}
|
||||
trustedIdKeyHashes = []string{}
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
tempDir := t.TempDir()
|
||||
|
||||
attestation := &tpmAttest.Attestation{
|
||||
Quotes: []*tpm.Quote{
|
||||
{
|
||||
Quote: []byte("test quote"),
|
||||
RawSig: []byte("test signature"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
binaryData, err := proto.Marshal(attestation)
|
||||
require.NoError(t, err)
|
||||
|
||||
attestationFile := filepath.Join(tempDir, "vtpm_attestation.pb")
|
||||
require.NoError(t, os.WriteFile(attestationFile, binaryData, 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
setupMock func(*mocks.Verifier)
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "successful verification",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifyAttestation", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "verification failure",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifyAttestation", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("verification failed"))
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
|
||||
cfgString = ""
|
||||
format = FormatBinaryPB
|
||||
|
||||
mockVerifier := new(mocks.Verifier)
|
||||
tt.setupMock(mockVerifier)
|
||||
|
||||
err := vtpmSevSnpverify(tt.args, mockVerifier)
|
||||
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
mockVerifier.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVtpmverify(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
attestation := &tpmAttest.Attestation{
|
||||
Quotes: []*tpm.Quote{
|
||||
{
|
||||
Quote: []byte("test quote"),
|
||||
RawSig: []byte("test signature"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
binaryData, err := proto.Marshal(attestation)
|
||||
require.NoError(t, err)
|
||||
|
||||
attestationFile := filepath.Join(tempDir, "vtpm_attestation.pb")
|
||||
require.NoError(t, os.WriteFile(attestationFile, binaryData, 0o644))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
setupMock func(*mocks.Verifier)
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "successful verification",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifVTpmAttestation", mock.Anything, mock.Anything).Return(nil)
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "verification failure",
|
||||
args: []string{attestationFile},
|
||||
setupMock: func(m *mocks.Verifier) {
|
||||
m.On("VerifVTpmAttestation", mock.Anything, mock.Anything).Return(fmt.Errorf("verification failed"))
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
format = FormatBinaryPB
|
||||
|
||||
mockVerifier := new(mocks.Verifier)
|
||||
tt.setupMock(mockVerifier)
|
||||
|
||||
err := vtpmverify(tt.args, mockVerifier)
|
||||
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
mockVerifier.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func uint32Ptr(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func uint64Ptr(v uint64) *uint64 {
|
||||
return &v
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
ccpb "github.com/google/go-tdx-guest/proto/checkconfig"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgTDX = &ccpb.Config{
|
||||
RootOfTrust: &ccpb.RootOfTrust{},
|
||||
Policy: &ccpb.Policy{HeaderPolicy: &ccpb.HeaderPolicy{}, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{}},
|
||||
}
|
||||
rtmrsS string
|
||||
trustedRootS string
|
||||
errNumberRtmrs = fmt.Errorf("expected 4 RTMRS values")
|
||||
errDecodeRtmrs = fmt.Errorf("failed to decode RTMRS hex string")
|
||||
errTrustedRootPath = fmt.Errorf("trusted root path must be a file, not a directory")
|
||||
errNotAFile = fmt.Errorf("trusted root path must be a file")
|
||||
)
|
||||
|
||||
func addTDXVerificationOptions(cmd *cobra.Command) *cobra.Command {
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.HeaderPolicy.QeVendorId,
|
||||
"qe_vendor_id",
|
||||
[]byte{},
|
||||
"The expected QE_VENDOR_ID field as a hex string. Must encode 16 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MrSeam,
|
||||
"mr_seam",
|
||||
[]byte{},
|
||||
"The expected MR_SEAM field as a hex string. Must encode 48 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.TdAttributes,
|
||||
"td_attributes",
|
||||
[]byte{},
|
||||
"The expected TD_ATTRIBUTES field as a hex string. Must encode 8 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.Xfam,
|
||||
"xfam",
|
||||
[]byte{},
|
||||
"The expected XFAM field as a hex string. Must encode 8 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MrTd,
|
||||
"mr_td",
|
||||
[]byte{},
|
||||
"The expected MR_TD field as a hex string. Must encode 48 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MrConfigId,
|
||||
"mr_config_id",
|
||||
[]byte{},
|
||||
"The expected MR_CONFIG_ID field as a hex string. Must encode 48 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MrOwnerConfig,
|
||||
"mr_owner",
|
||||
[]byte{},
|
||||
"The expected MR_OWNER field as a hex string. Must encode 48 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MrOwnerConfig,
|
||||
"mr_config_owner",
|
||||
[]byte{},
|
||||
"The expected MR_OWNER_CONFIG field as a hex string. Must encode 48 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().BytesHexVar(
|
||||
&cfgTDX.Policy.TdQuoteBodyPolicy.MinimumTeeTcbSvn,
|
||||
"minimum_tee_tcb_svn",
|
||||
[]byte{},
|
||||
"The minimum acceptable value for TEE_TCB_SVN field as a hex string. Must encode 16 bytes. Unchecked if unset.",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&rtmrsS,
|
||||
"rtmrs",
|
||||
"",
|
||||
"Comma-separated hex strings representing expected values of RTMRS field. Expected 4 strings, either empty or each must encode 48 bytes. Unchecked if unset",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&trustedRootS,
|
||||
"trusted_root",
|
||||
"",
|
||||
"Comma-separated paths to CA bundles for the Intel TDX. Must be in PEM format, Root CA certificate. If unset, uses embedded root certificate.",
|
||||
)
|
||||
cmd.Flags().Uint32Var(
|
||||
&cfgTDX.Policy.HeaderPolicy.MinimumQeSvn,
|
||||
"minimum_qe_svn",
|
||||
0,
|
||||
"The minimum acceptable value for QE_SVN field.",
|
||||
)
|
||||
cmd.Flags().Uint32Var(
|
||||
&cfgTDX.Policy.HeaderPolicy.MinimumPceSvn,
|
||||
"minimum_pce_svn",
|
||||
0,
|
||||
"The minimum acceptable value for PCE_SVN field.",
|
||||
)
|
||||
cmd.Flags().BoolVar(
|
||||
&cfgTDX.RootOfTrust.GetCollateral,
|
||||
"get_collateral",
|
||||
false,
|
||||
"If true, then permitted to download necessary collaterals for additional checks.",
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func parseRtmrs() ([][]byte, error) {
|
||||
if rtmrsS == "" {
|
||||
return nil, nil // No RTMRS provided, return nil
|
||||
}
|
||||
|
||||
hexString := strings.Split(rtmrsS, ",")
|
||||
if len(hexString) != 4 {
|
||||
return nil, errNumberRtmrs
|
||||
}
|
||||
|
||||
var result [][]byte
|
||||
for _, hexStr := range hexString {
|
||||
h, err := hex.DecodeString(strings.TrimSpace(hexStr))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errDecodeRtmrs, err)
|
||||
}
|
||||
|
||||
result = append(result, h)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func parseTrustedRoot() ([]string, error) {
|
||||
if trustedRootS == "" {
|
||||
return nil, nil // No trusted roots provided, return nil
|
||||
}
|
||||
|
||||
roots := strings.Split(trustedRootS, ",")
|
||||
var result []string
|
||||
for _, root := range roots {
|
||||
p := strings.TrimSpace(root)
|
||||
state, err := os.Stat(p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errTrustedRootPath, err)
|
||||
}
|
||||
if state.IsDir() {
|
||||
return nil, errNotAFile
|
||||
}
|
||||
|
||||
result = append(result, p)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func parseTDXConfig() error {
|
||||
if cfgString == "" {
|
||||
return nil // No config provided, return nil
|
||||
}
|
||||
|
||||
policyByte, err := os.ReadFile(cfgString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := protojson.Unmarshal(policyByte, cfgTDX); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateTDXFlags() error {
|
||||
if err := parseTDXConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rtrms, err := parseRtmrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rtrms != nil {
|
||||
cfgTDX.Policy.TdQuoteBodyPolicy.Rtmrs = rtrms
|
||||
}
|
||||
trustedRoots, err := parseTrustedRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if trustedRoots != nil {
|
||||
cfgTDX.RootOfTrust.CabundlePaths = trustedRoots
|
||||
}
|
||||
|
||||
if err := validateTDXinput(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tdxVerify(reportFilePath string, verifier attestation.Verifier) error {
|
||||
attestationFile = reportFilePath
|
||||
input, err := openInputFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if closer, ok := input.(*os.File); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
attestationBytes, err := io.ReadAll(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return verifier.VerifyAttestation(attestationBytes, reportData, nil)
|
||||
}
|
||||
|
||||
func validateTDXinput() error {
|
||||
if err := validateFieldLength("qe_vendor_id", cfgTDX.Policy.HeaderPolicy.QeVendorId, size16); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("mr_seam", cfgTDX.Policy.TdQuoteBodyPolicy.MrSeam, size48); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("td_attributes", cfgTDX.Policy.TdQuoteBodyPolicy.TdAttributes, size8); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("xfam", cfgTDX.Policy.TdQuoteBodyPolicy.Xfam, size8); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("mr_td", cfgTDX.Policy.TdQuoteBodyPolicy.MrTd, size48); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("mr_config_id", cfgTDX.Policy.TdQuoteBodyPolicy.MrConfigId, size48); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("mr_owner", cfgTDX.Policy.TdQuoteBodyPolicy.MrOwnerConfig, size48); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("mr_config_owner", cfgTDX.Policy.TdQuoteBodyPolicy.MrOwnerConfig, size48); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFieldLength("minimum_tee_tcb_svn", cfgTDX.Policy.TdQuoteBodyPolicy.MinimumTeeTcbSvn, size16); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+801
-38
@@ -4,13 +4,15 @@ package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/proto/sevsnp"
|
||||
@@ -18,6 +20,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
mmocks "github.com/ultravioletrs/cocos/pkg/attestation/cmdconfig/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"github.com/ultravioletrs/cocos/pkg/sdk/mocks"
|
||||
@@ -49,6 +52,7 @@ func TestNewGetAttestationCmd(t *testing.T) {
|
||||
|
||||
teeNonce := hex.EncodeToString(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce))
|
||||
vtpmNonce := hex.EncodeToString(bytes.Repeat([]byte{0x00}, vtpm.Nonce))
|
||||
tokenNonce := hex.EncodeToString(bytes.Repeat([]byte{0x00}, vtpm.Nonce))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -102,7 +106,7 @@ func TestNewGetAttestationCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid vTPM data size",
|
||||
args: []string{"vtpm", "-t", hex.EncodeToString(bytes.Repeat([]byte{0x00}, 33))},
|
||||
args: []string{"vtpm", "--vtpm", hex.EncodeToString(bytes.Repeat([]byte{0x00}, 33))},
|
||||
mockResponse: nil,
|
||||
mockError: errors.New("error"),
|
||||
expectedErr: "vTPM nonce must be a hex encoded string of length lesser or equal 32 bytes",
|
||||
@@ -116,39 +120,60 @@ func TestNewGetAttestationCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "failed to get attestation",
|
||||
args: []string{"snp", "-e", teeNonce},
|
||||
args: []string{"snp", "--tee", teeNonce},
|
||||
mockResponse: nil,
|
||||
mockError: errors.New("error"),
|
||||
expectedErr: "Failed to get attestation due to error",
|
||||
},
|
||||
{
|
||||
name: "Textproto report error",
|
||||
args: []string{"snp", "-e", teeNonce, "--textproto"},
|
||||
args: []string{"snp", "--tee", teeNonce, "--reporttextproto"},
|
||||
mockResponse: []byte("mock attestation"),
|
||||
mockError: nil,
|
||||
expectedErr: "Error converting attestation to textproto",
|
||||
expectedErr: "Fetching SEV-SNP attestation report\nError converting SNP attestation to JSON: attestation contents too small : attestation contents too small (0x10 bytes). Want at least 0x4a0 bytes ❌\n",
|
||||
},
|
||||
{
|
||||
name: "successful Textproto report",
|
||||
args: []string{"snp", "-e", teeNonce, "--textproto"},
|
||||
args: []string{"snp", "--tee", teeNonce, "--reporttextproto"},
|
||||
mockResponse: validattestation,
|
||||
mockError: nil,
|
||||
expectedOut: "Attestation result retrieved and saved successfully!",
|
||||
},
|
||||
{
|
||||
name: "connection error",
|
||||
args: []string{"snp", "-e", teeNonce},
|
||||
args: []string{"snp", "--tee", teeNonce},
|
||||
mockResponse: nil,
|
||||
mockError: errors.New("failed to connect to agent"),
|
||||
expectedErr: "Failed to connect to agent",
|
||||
},
|
||||
{
|
||||
name: "successful Azure token retrieval",
|
||||
args: []string{"azure-token", "--token", tokenNonce},
|
||||
mockResponse: []byte("eyJhbGciOiAiUlMyNTYifQ.eyJzdWIiOiAidGVzdC11c2VyIn0.signature"),
|
||||
mockError: nil,
|
||||
expectedOut: "Fetching Azure token\nAttestation result retrieved and saved successfully!\n",
|
||||
},
|
||||
{
|
||||
name: "failed to retrieve Azure token",
|
||||
args: []string{"azure-token", "--token", tokenNonce},
|
||||
mockResponse: nil,
|
||||
mockError: errors.New("error"),
|
||||
expectedErr: "Fetching Azure token\nFailed to get attestation result due to error: error ❌\n",
|
||||
},
|
||||
{
|
||||
name: "invalid token nonce size",
|
||||
args: []string{"azure-token", "--token", hex.EncodeToString(bytes.Repeat([]byte{0x00}, 33))},
|
||||
mockResponse: nil,
|
||||
mockError: errors.New("error"),
|
||||
expectedErr: "Fetching Azure token\nvTPM nonce must be a hex encoded string of length lesser or equal 32 bytes ❌ \n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
os.Remove(attestationFilePath)
|
||||
os.Remove(attestationJson)
|
||||
os.Remove(attestationReportJson)
|
||||
})
|
||||
mockSDK := new(mocks.SDK)
|
||||
cli := &CLI{agentSDK: mockSDK}
|
||||
@@ -164,6 +189,11 @@ func TestNewGetAttestationCmd(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
mockSDK.On("AttestationResult", mock.Anything, [vtpm.Nonce]byte(bytes.Repeat([]byte{0x00}, vtpm.Nonce)), mock.Anything, mock.Anything).Return(tc.mockError).Run(func(args mock.Arguments) {
|
||||
_, err := args.Get(3).(*os.File).Write(tc.mockResponse)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
|
||||
@@ -182,7 +212,8 @@ func TestNewValidateAttestationValidationCmdDefaults(t *testing.T) {
|
||||
cmd := cli.NewValidateAttestationValidationCmd()
|
||||
|
||||
assert.Equal(t, "validate", cmd.Use)
|
||||
assert.Equal(t, "Validate and verify attestation information. You can choose from 3 modes: snp,vtpm and snp-vtpm.Default mode is snp.", cmd.Short)
|
||||
expectedMessage := fmt.Sprintf("Validate and verify attestation information. You can define the confidential computing cloud provider (%s, %s, %s; %s is the default) and can choose from 4 modes: %s, %s, %s, and %s. Default mode is %s.", CCNone, CCAzure, CCGCP, CCNone, SNP, VTPM, SNPvTPM, TDX, SNP)
|
||||
assert.Equal(t, expectedMessage, cmd.Short)
|
||||
|
||||
assert.Equal(t, fmt.Sprint(defaultMinimumTcb), cmd.Flag("minimum_tcb").Value.String())
|
||||
assert.Equal(t, fmt.Sprint(defaultMinimumLaunchTcb), cmd.Flag("minimum_lauch_tcb").Value.String())
|
||||
@@ -283,26 +314,12 @@ func TestNewValidateAttestationValidationCmd(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
type MockMeasurement struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockMeasurement) Run(igvmBinaryPath string) error {
|
||||
args := m.Called(igvmBinaryPath)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockMeasurement) Stop() error {
|
||||
args := m.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestNewMeasureCmd_RunSuccess(t *testing.T) {
|
||||
cliInstance := &CLI{}
|
||||
mockMeasurement := new(MockMeasurement)
|
||||
mockMeasurement := new(mmocks.MeasurementProvider)
|
||||
cliInstance.measurement = mockMeasurement
|
||||
|
||||
mockMeasurement.On("Run", "testfile.igvm").Return(nil)
|
||||
mockMeasurement.On("Run", "testfile.igvm").Return([]byte{}, nil)
|
||||
|
||||
cmd := cliInstance.NewMeasureCmd("fake_binary_path")
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -318,11 +335,11 @@ func TestNewMeasureCmd_RunSuccess(t *testing.T) {
|
||||
|
||||
func TestNewMeasureCmd_RunError(t *testing.T) {
|
||||
cliInstance := &CLI{}
|
||||
mockMeasurement := new(MockMeasurement)
|
||||
mockMeasurement := new(mmocks.MeasurementProvider)
|
||||
cliInstance.measurement = mockMeasurement
|
||||
expectedError := errors.New("mocked measurement error")
|
||||
|
||||
mockMeasurement.On("Run", "testfile.igvm").Return(expectedError)
|
||||
mockMeasurement.On("Run", "testfile.igvm").Return([]byte{}, expectedError)
|
||||
|
||||
cmd := cliInstance.NewMeasureCmd("fake_binary_path")
|
||||
|
||||
@@ -338,25 +355,34 @@ func TestNewMeasureCmd_RunError(t *testing.T) {
|
||||
mockMeasurement.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestParseConfig(t *testing.T) {
|
||||
func TestParseConfig1(t *testing.T) {
|
||||
tmpfile, err := os.CreateTemp("", "attestation_policy.json")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
cfgString = ""
|
||||
err := parseConfig()
|
||||
|
||||
err = parseConfig()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, cfg.RootOfTrust)
|
||||
assert.NotNil(t, cfg.Policy)
|
||||
|
||||
cfgString = `{"rootOfTrust":{"product":"test_product"},"policy":{"minimumGuestSvn":1}}`
|
||||
cfgString = tmpfile.Name()
|
||||
|
||||
_, err = tmpfile.WriteString(`{"rootOfTrust":{"product":"test_product"},"policy":{"minimumGuestSvn":1}}`)
|
||||
require.NoError(t, err)
|
||||
err = parseConfig()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test_product", cfg.RootOfTrust.Product)
|
||||
assert.Equal(t, uint32(1), cfg.Policy.MinimumGuestSvn)
|
||||
|
||||
cfgString = `{"invalid_json"`
|
||||
_, err = tmpfile.WriteString(`{"invalid_json"`)
|
||||
require.NoError(t, err)
|
||||
err = parseConfig()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParseHashes(t *testing.T) {
|
||||
func TestParseHashes1(t *testing.T) {
|
||||
trustedAuthorHashes = []string{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}
|
||||
trustedIdKeyHashes = []string{"fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"}
|
||||
|
||||
@@ -390,9 +416,11 @@ func TestParseFiles(t *testing.T) {
|
||||
trustedAuthorKeys = []string{authorKeyFile}
|
||||
trustedIdKeys = []string{idKeyFile}
|
||||
|
||||
err = parseFiles()
|
||||
err = parseAttestationFile()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("test attestation"), attestation)
|
||||
err = parseTrustedKeys()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("test attestation"), attestationRaw)
|
||||
assert.Len(t, cfg.Policy.TrustedAuthorKeys, 1)
|
||||
assert.Len(t, cfg.Policy.TrustedIdKeys, 1)
|
||||
|
||||
@@ -401,11 +429,11 @@ func TestParseFiles(t *testing.T) {
|
||||
os.Remove(idKeyFile)
|
||||
|
||||
attestationFile = "non_existent_file.bin"
|
||||
err = parseFiles()
|
||||
err = parseAttestationFile()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParseUints(t *testing.T) {
|
||||
func TestParseUints1(t *testing.T) {
|
||||
stepping = "10"
|
||||
platformInfo = "0xFF"
|
||||
|
||||
@@ -430,7 +458,7 @@ func TestParseUints(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestValidateInput(t *testing.T) {
|
||||
func TestValidateInput1(t *testing.T) {
|
||||
cfg = check.Config{}
|
||||
if cfg.Policy == nil {
|
||||
cfg.Policy = &check.Policy{}
|
||||
@@ -455,7 +483,7 @@ func TestValidateInput(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetBase(t *testing.T) {
|
||||
func TestGetBase1(t *testing.T) {
|
||||
assert.Equal(t, 16, getBase("0xFF"))
|
||||
assert.Equal(t, 8, getBase("0o77"))
|
||||
assert.Equal(t, 2, getBase("0b1010"))
|
||||
@@ -624,3 +652,738 @@ func TestRoundTrip(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, roundTripReport)
|
||||
}
|
||||
|
||||
func TestDecodeJWTToJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
err error
|
||||
validate func(t *testing.T, output []byte)
|
||||
}{
|
||||
{
|
||||
name: "Valid JWT",
|
||||
input: []byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." +
|
||||
"SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"),
|
||||
err: nil,
|
||||
validate: func(t *testing.T, output []byte) {
|
||||
assert.NotEmpty(t, output)
|
||||
assert.Contains(t, string(output), `"header"`)
|
||||
assert.Contains(t, string(output), `"payload"`)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid JWT - one part",
|
||||
input: []byte("justonepart"),
|
||||
err: fmt.Errorf("invalid JWT: must have at least 2 parts"),
|
||||
validate: func(t *testing.T, output []byte) {
|
||||
assert.Nil(t, output)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid Base64",
|
||||
input: []byte("bad@@@.header"),
|
||||
err: errors.New("illegal base64 data"),
|
||||
validate: func(t *testing.T, output []byte) {
|
||||
assert.Nil(t, output)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := decodeJWTToJSON(tt.input)
|
||||
|
||||
if tt.err != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
tt.validate(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestEnvironment() func() {
|
||||
originalMode := mode
|
||||
originalCfgString := cfgString
|
||||
originalTimeout := timeout
|
||||
originalMaxRetryDelay := maxRetryDelay
|
||||
originalPlatformInfo := platformInfo
|
||||
originalStepping := stepping
|
||||
originalTrustedAuthorKeys := trustedAuthorKeys
|
||||
originalTrustedAuthorHashes := trustedAuthorHashes
|
||||
originalTrustedIdKeys := trustedIdKeys
|
||||
originalTrustedIdKeyHashes := trustedIdKeyHashes
|
||||
originalAttestationFile := attestationFile
|
||||
originalAttestationRaw := attestationRaw
|
||||
originalOutput := output
|
||||
originalNonce := nonce
|
||||
originalFormat := format
|
||||
originalTeeNonce := teeNonce
|
||||
originalTokenNonce := tokenNonce
|
||||
originalGetTextProtoAttestationReport := getTextProtoAttestationReport
|
||||
originalGetAzureTokenJWT := getAzureTokenJWT
|
||||
originalCloud := cloud
|
||||
originalReportData := reportData
|
||||
originalCheckCrl := checkCrl
|
||||
|
||||
mode = ""
|
||||
cfgString = ""
|
||||
timeout = 0
|
||||
maxRetryDelay = 0
|
||||
platformInfo = ""
|
||||
stepping = ""
|
||||
trustedAuthorKeys = []string{}
|
||||
trustedAuthorHashes = []string{}
|
||||
trustedIdKeys = []string{}
|
||||
trustedIdKeyHashes = []string{}
|
||||
attestationFile = ""
|
||||
attestationRaw = []byte{}
|
||||
output = ""
|
||||
nonce = []byte{}
|
||||
format = ""
|
||||
teeNonce = []byte{}
|
||||
tokenNonce = []byte{}
|
||||
getTextProtoAttestationReport = false
|
||||
getAzureTokenJWT = false
|
||||
cloud = ""
|
||||
reportData = []byte{}
|
||||
checkCrl = false
|
||||
|
||||
return func() {
|
||||
mode = originalMode
|
||||
cfgString = originalCfgString
|
||||
timeout = originalTimeout
|
||||
maxRetryDelay = originalMaxRetryDelay
|
||||
platformInfo = originalPlatformInfo
|
||||
stepping = originalStepping
|
||||
trustedAuthorKeys = originalTrustedAuthorKeys
|
||||
trustedAuthorHashes = originalTrustedAuthorHashes
|
||||
trustedIdKeys = originalTrustedIdKeys
|
||||
trustedIdKeyHashes = originalTrustedIdKeyHashes
|
||||
attestationFile = originalAttestationFile
|
||||
attestationRaw = originalAttestationRaw
|
||||
output = originalOutput
|
||||
nonce = originalNonce
|
||||
format = originalFormat
|
||||
teeNonce = originalTeeNonce
|
||||
tokenNonce = originalTokenNonce
|
||||
getTextProtoAttestationReport = originalGetTextProtoAttestationReport
|
||||
getAzureTokenJWT = originalGetAzureTokenJWT
|
||||
cloud = originalCloud
|
||||
reportData = originalReportData
|
||||
checkCrl = originalCheckCrl
|
||||
}
|
||||
}
|
||||
|
||||
func createTempFile(t *testing.T, content []byte) string {
|
||||
tmpfile, err := os.CreateTemp("", "test_*.bin")
|
||||
require.NoError(t, err)
|
||||
defer tmpfile.Close()
|
||||
|
||||
_, err = tmpfile.Write(content)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tmpfile.Name()
|
||||
}
|
||||
|
||||
func TestNewAttestationCmdEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedOutput string
|
||||
hasSubcommands bool
|
||||
}{
|
||||
{
|
||||
name: "no arguments shows help",
|
||||
args: []string{},
|
||||
expectedOutput: "Get and validate attestations",
|
||||
hasSubcommands: true,
|
||||
},
|
||||
{
|
||||
name: "help flag shows usage",
|
||||
args: []string{"--help"},
|
||||
expectedOutput: "Get and validate attestations",
|
||||
hasSubcommands: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSDK := new(mocks.SDK)
|
||||
cli := &CLI{agentSDK: mockSDK}
|
||||
cmd := cli.NewAttestationCmd()
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
cmd.SetArgs(tt.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, tt.expectedOutput)
|
||||
|
||||
if tt.hasSubcommands {
|
||||
assert.Contains(t, output, "Get and validate attestations")
|
||||
assert.Contains(t, output, "Usage:")
|
||||
assert.Contains(t, output, "Flags:")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAttestationCmdEdgeCases(t *testing.T) {
|
||||
defer setupTestEnvironment()()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
setupMock func(*mocks.SDK)
|
||||
expectedErr string
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
name: "no arguments provided",
|
||||
args: []string{},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "accepts 1 arg(s), received 0",
|
||||
},
|
||||
{
|
||||
name: "too many arguments",
|
||||
args: []string{"snp", "extra"},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "accepts 1 arg(s), received 2",
|
||||
},
|
||||
{
|
||||
name: "invalid attestation type",
|
||||
args: []string{"invalid-type"},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "Bad attestation type",
|
||||
},
|
||||
{
|
||||
name: "SNP with missing TEE nonce",
|
||||
args: []string{"snp"},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "TEE nonce must be defined for SEV-SNP attestation",
|
||||
},
|
||||
{
|
||||
name: "vTPM with missing nonce",
|
||||
args: []string{"vtpm"},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "vTPM nonce must be defined for vTPM attestation",
|
||||
},
|
||||
{
|
||||
name: "Azure token with missing token nonce",
|
||||
args: []string{"azure-token"},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "Token nonce must be defined for Azure attestation",
|
||||
},
|
||||
{
|
||||
name: "TEE nonce too large",
|
||||
args: []string{"snp", "--tee", hex.EncodeToString(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce+1))},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "nonce must be a hex encoded string of length lesser or equal 64 bytes",
|
||||
},
|
||||
{
|
||||
name: "vTPM nonce too large",
|
||||
args: []string{"vtpm", "--vtpm", hex.EncodeToString(bytes.Repeat([]byte{0x00}, vtpm.Nonce+1))},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "vTPM nonce must be a hex encoded string of length lesser or equal 32 bytes",
|
||||
},
|
||||
{
|
||||
name: "Token nonce too large",
|
||||
args: []string{"azure-token", "--token", hex.EncodeToString(bytes.Repeat([]byte{0x00}, vtpm.Nonce+1))},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "vTPM nonce must be a hex encoded string of length lesser or equal 32 bytes",
|
||||
},
|
||||
{
|
||||
name: "successful TDX attestation",
|
||||
args: []string{"tdx", "--tee", hex.EncodeToString(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce))},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
sdk.On("Attestation", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(nil).Run(func(args mock.Arguments) {
|
||||
if _, err := args.Get(4).(*os.File).Write([]byte("mock tdx attestation")); err != nil {
|
||||
t.Fatalf("Failed to write to attestation file: %v", err)
|
||||
}
|
||||
})
|
||||
},
|
||||
expectedOut: "Fetching TDX attestation report",
|
||||
},
|
||||
{
|
||||
name: "file creation error",
|
||||
args: []string{"snp", "--tee", hex.EncodeToString(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce))},
|
||||
setupMock: func(sdk *mocks.SDK) {
|
||||
},
|
||||
expectedErr: "Error creating attestation file",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
os.Remove(attestationFilePath)
|
||||
os.Remove(azureAttestResultFilePath)
|
||||
os.Remove(azureAttestTokenFilePath)
|
||||
defer func() {
|
||||
os.Remove(attestationFilePath)
|
||||
os.Remove(azureAttestResultFilePath)
|
||||
os.Remove(azureAttestTokenFilePath)
|
||||
}()
|
||||
|
||||
mockSDK := new(mocks.SDK)
|
||||
cli := &CLI{agentSDK: mockSDK}
|
||||
tc.setupMock(mockSDK)
|
||||
|
||||
if tc.name == "file creation error" {
|
||||
err := os.Mkdir(attestationFilePath, 0o755)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(attestationFilePath)
|
||||
}
|
||||
|
||||
cmd := cli.NewGetAttestationCmd()
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
output := buf.String()
|
||||
|
||||
if tc.expectedErr != "" {
|
||||
assert.Contains(t, output, tc.expectedErr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tc.expectedOut != "" {
|
||||
assert.Contains(t, output, tc.expectedOut)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileOperations(t *testing.T) {
|
||||
defer setupTestEnvironment()()
|
||||
|
||||
t.Run("openInputFile", func(t *testing.T) {
|
||||
attestationFile = ""
|
||||
reader, err := openInputFile()
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, errEmptyFile, err)
|
||||
assert.Nil(t, reader)
|
||||
|
||||
tempFile := createTempFile(t, []byte("test content"))
|
||||
defer os.Remove(tempFile)
|
||||
attestationFile = tempFile
|
||||
reader, err = openInputFile()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, reader)
|
||||
if file, ok := reader.(*os.File); ok {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
attestationFile = "non-existent-file.bin"
|
||||
reader, err = openInputFile()
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, reader)
|
||||
})
|
||||
|
||||
t.Run("createOutputFile", func(t *testing.T) {
|
||||
output = ""
|
||||
writer, err := createOutputFile()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, os.Stdout, writer)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
output = filepath.Join(tempDir, "test_output.txt")
|
||||
writer, err = createOutputFile()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, writer)
|
||||
if file, ok := writer.(*os.File); ok {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
output = "/invalid/path/file.txt"
|
||||
writer, err = createOutputFile()
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, writer)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidationFunctions(t *testing.T) {
|
||||
t.Run("validateFieldLength", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fieldName string
|
||||
field []byte
|
||||
expectedLength int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "nil field",
|
||||
fieldName: "test",
|
||||
field: nil,
|
||||
expectedLength: 32,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "correct length",
|
||||
fieldName: "test",
|
||||
field: make([]byte, 32),
|
||||
expectedLength: 32,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "incorrect length",
|
||||
fieldName: "test",
|
||||
field: make([]byte, 16),
|
||||
expectedLength: 32,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateFieldLength(tt.fieldName, tt.field, tt.expectedLength)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.fieldName)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecodeJWTToJSONEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
expected string
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "empty input",
|
||||
input: []byte(""),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "single part",
|
||||
input: []byte("onlyonepart"),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid base64 in header",
|
||||
input: []byte("invalid@base64.validpart"),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid base64 in payload",
|
||||
input: []byte("eyJhbGciOiJIUzI1NiJ9.invalid@base64"),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid JSON in header",
|
||||
input: []byte("bm90anNvbg.eyJzdWIiOiJ0ZXN0In0"),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid JSON in payload",
|
||||
input: []byte("eyJhbGciOiJIUzI1NiJ9.bm90anNvbg"),
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "valid JWT with padding",
|
||||
input: []byte("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0In0.signature"),
|
||||
expected: `{
|
||||
"header": {
|
||||
"alg": "HS256"
|
||||
},
|
||||
"payload": {
|
||||
"sub": "test"
|
||||
}
|
||||
}`,
|
||||
hasError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := decodeJWTToJSON(tt.input)
|
||||
if tt.hasError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
if tt.expected != "" {
|
||||
assert.JSONEq(t, tt.expected, string(result))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasureCmdEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
mockSetup func(*mmocks.MeasurementProvider)
|
||||
expectedError string
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
name: "no arguments",
|
||||
args: []string{},
|
||||
mockSetup: func(m *mmocks.MeasurementProvider) {
|
||||
},
|
||||
expectedError: "requires at least 1 arg(s), only received 0",
|
||||
},
|
||||
{
|
||||
name: "single line output success",
|
||||
args: []string{"test.igvm"},
|
||||
mockSetup: func(m *mmocks.MeasurementProvider) {
|
||||
m.On("Run", "test.igvm").Return([]byte("ABCDEF123456"), nil)
|
||||
},
|
||||
expectedOut: "",
|
||||
},
|
||||
{
|
||||
name: "multi-line output error",
|
||||
args: []string{"test.igvm"},
|
||||
mockSetup: func(m *mmocks.MeasurementProvider) {
|
||||
m.On("Run", "test.igvm").Return([]byte("line1\nline2\nERROR: something went wrong"), nil)
|
||||
},
|
||||
expectedError: "ERROR: something went wrong",
|
||||
},
|
||||
{
|
||||
name: "measurement run error",
|
||||
args: []string{"test.igvm"},
|
||||
mockSetup: func(m *mmocks.MeasurementProvider) {
|
||||
m.On("Run", "test.igvm").Return(nil, errors.New("measurement failed"))
|
||||
},
|
||||
expectedError: "measurement failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockMeasurement := new(mmocks.MeasurementProvider)
|
||||
tt.mockSetup(mockMeasurement)
|
||||
|
||||
cli := &CLI{measurement: mockMeasurement}
|
||||
cmd := cli.NewMeasureCmd("fake_binary_path")
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
cmd.SetArgs(tt.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
|
||||
if tt.expectedError != "" {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedError)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tt.expectedOut != "" {
|
||||
assert.Contains(t, buf.String(), tt.expectedOut)
|
||||
}
|
||||
}
|
||||
|
||||
mockMeasurement.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAttestationValidationCmdPreRunE(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "no file path provided",
|
||||
args: []string{},
|
||||
flags: map[string]string{"mode": "snp"},
|
||||
expectedErr: "please pass the attestation report file path",
|
||||
},
|
||||
{
|
||||
name: "multiple file paths",
|
||||
args: []string{"file1.bin", "file2.bin"},
|
||||
flags: map[string]string{"mode": "snp"},
|
||||
expectedErr: "please pass the attestation report file path",
|
||||
},
|
||||
{
|
||||
name: "unknown mode",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "unknown"},
|
||||
expectedErr: "unknown mode: unknown",
|
||||
},
|
||||
{
|
||||
name: "SNP mode missing report_data",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "snp"},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "SNP mode missing product",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "snp", "report_data": "123"},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "vTPM mode missing nonce",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "vtpm"},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "SNP-vTPM mode missing required flags",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "snp-vtpm"},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "TDX mode missing report_data",
|
||||
args: []string{"test.bin"},
|
||||
flags: map[string]string{"mode": "tdx"},
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
cmd := cli.NewValidateAttestationValidationCmd()
|
||||
|
||||
for key, value := range tt.flags {
|
||||
if err := cmd.Flags().Set(key, value); err != nil {
|
||||
}
|
||||
}
|
||||
|
||||
err := cmd.PreRunE(cmd, tt.args)
|
||||
if tt.expectedErr != "" {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudProviderConfigurations(t *testing.T) {
|
||||
defer setupTestEnvironment()()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cloud string
|
||||
expectedType string
|
||||
}{
|
||||
{
|
||||
name: "none cloud provider",
|
||||
cloud: CCNone,
|
||||
expectedType: "vtpm",
|
||||
},
|
||||
{
|
||||
name: "azure cloud provider",
|
||||
cloud: CCAzure,
|
||||
expectedType: "azure",
|
||||
},
|
||||
{
|
||||
name: "gcp cloud provider",
|
||||
cloud: CCGCP,
|
||||
expectedType: "vtpm",
|
||||
},
|
||||
{
|
||||
name: "default cloud provider",
|
||||
cloud: "",
|
||||
expectedType: "vtpm",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
cmd := cli.NewValidateAttestationValidationCmd()
|
||||
|
||||
if err := cmd.Flags().Set("cloud", tt.cloud); err != nil {
|
||||
t.Fatalf("Failed to set cloud flag: %v", err)
|
||||
}
|
||||
cloud, _ := cmd.Flags().GetString("cloud")
|
||||
assert.Equal(t, tt.cloud, cloud)
|
||||
|
||||
assert.Contains(t, cmd.Short, tt.cloud)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileOperationErrors(t *testing.T) {
|
||||
defer setupTestEnvironment()()
|
||||
|
||||
t.Run("file close error handling", func(t *testing.T) {
|
||||
tempFile := createTempFile(t, []byte("test content"))
|
||||
defer os.Remove(tempFile)
|
||||
|
||||
assert.True(t, true)
|
||||
})
|
||||
|
||||
t.Run("file write error handling", func(t *testing.T) {
|
||||
tempFile := createTempFile(t, []byte("test content"))
|
||||
defer os.Remove(tempFile)
|
||||
|
||||
err := os.Chmod(tempFile, 0o444)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = os.WriteFile(tempFile, []byte("new content"), 0o644)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("file read error handling", func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
_, err := os.ReadFile(tempDir)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestContextCancellation(t *testing.T) {
|
||||
defer setupTestEnvironment()()
|
||||
|
||||
mockSDK := new(mocks.SDK)
|
||||
cli := &CLI{agentSDK: mockSDK}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
mockSDK.On("Attestation", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(context.Canceled)
|
||||
|
||||
cmd := cli.NewGetAttestationCmd()
|
||||
cmd.SetContext(ctx)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
teeNonceHex := hex.EncodeToString(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce))
|
||||
cmd.SetArgs([]string{"snp", "--tee", teeNonceHex})
|
||||
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, buf.String(), "Failed to get attestation due to error")
|
||||
}
|
||||
|
||||
+5
-4
@@ -12,7 +12,8 @@ import (
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/verify/trust"
|
||||
"github.com/spf13/cobra"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,14 +28,14 @@ func (cli *CLI) NewCABundleCmd(fileSavePath string) *cobra.Command {
|
||||
Example: "ca-bundle <path_to_platform_info_json>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
attestationConfiguration := config.Config{SnpCheck: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}, PcrConfig: &config.PcrConfig{}}
|
||||
err := config.ReadAttestationPolicy(args[0], &attestationConfiguration)
|
||||
attestationConfiguration := attestation.Config{Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
err := vtpm.ReadPolicy(args[0], &attestationConfiguration)
|
||||
if err != nil {
|
||||
printError(cmd, "Error while reading manifest: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
product := attestationConfiguration.SnpCheck.RootOfTrust.ProductLine
|
||||
product := attestationConfiguration.Config.RootOfTrust.ProductLine
|
||||
|
||||
getter := trust.DefaultHTTPSGetter()
|
||||
caURL := kds.ProductCertChainURL(abi.VcekReportSigner, product)
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/agent/auth"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
mgerrors "github.com/absmach/magistrala/pkg/errors"
|
||||
mgerrors "github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/agent/auth"
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
const (
|
||||
imaMeasurementsFilename = "ima_measurements"
|
||||
)
|
||||
|
||||
func (cli *CLI) NewIMAMeasurementsCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "ima-measurements",
|
||||
Short: "Retrieve Linux IMA measurements file",
|
||||
Example: "ima-measurements <optional_file_name>",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if cli.connectErr != nil {
|
||||
printError(cmd, "Failed to connect to agent: %v ❌ ", cli.connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println("⏳ Retrieving computation Linux IMA measurements file")
|
||||
|
||||
filename := imaMeasurementsFilename
|
||||
if len(args) >= 1 {
|
||||
filename = args[0]
|
||||
}
|
||||
|
||||
imaMeasurementsFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
printError(cmd, "Error creating imaMeasurements file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
defer imaMeasurementsFile.Close()
|
||||
|
||||
pcr10, err := cli.agentSDK.IMAMeasurements(cmd.Context(), imaMeasurementsFile)
|
||||
if err != nil {
|
||||
printError(cmd, "Error retrieving Linux IMA measurements file: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println(color.New(color.FgGreen).Sprintf("Linux IMA measurements file retrieved and saved successfully as %s! PCR10 = %s ✔ ", filename, hex.EncodeToString(pcr10)))
|
||||
|
||||
calculatedPCR10 := make([]byte, vtpm.Hash1)
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
printError(cmd, "Failed to open file: %v ❌ ", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
|
||||
if parts[0] != "10" {
|
||||
continue
|
||||
}
|
||||
|
||||
digestHex := parts[1]
|
||||
if digestHex == strings.Repeat("0", 40) {
|
||||
digestHex = strings.Repeat("f", 40)
|
||||
}
|
||||
|
||||
digest, err := hex.DecodeString(digestHex)
|
||||
if err != nil {
|
||||
printError(cmd, "Failed to decode digest: %v ❌ ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
hasher := sha1.New()
|
||||
hasher.Write(calculatedPCR10)
|
||||
hasher.Write(digest)
|
||||
calculatedPCR10 = hasher.Sum(nil)
|
||||
}
|
||||
|
||||
if hex.EncodeToString(pcr10) != hex.EncodeToString(calculatedPCR10) {
|
||||
printError(cmd, "Measurements file not verified ❌ ", err)
|
||||
} else {
|
||||
cmd.Println(color.New(color.FgGreen).Sprintf("Measurements file verified!"))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/pkg/sdk/mocks"
|
||||
)
|
||||
|
||||
func TestCLI_NewIMAMeasurementsCmd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
connectErr error
|
||||
mockIMAData string
|
||||
mockError error
|
||||
expectedFilename string
|
||||
expectedOutput []string
|
||||
expectedError []string
|
||||
shouldCreateFile bool
|
||||
fileCreationError bool
|
||||
invalidDigestData bool
|
||||
setupCustomFile func(filename string) error
|
||||
}{
|
||||
{
|
||||
name: "successful_retrieval_default_filename",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockIMAData: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
mockError: nil,
|
||||
expectedFilename: imaMeasurementsFilename,
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "Linux IMA measurements file retrieved and saved successfully", "PCR10 = 0000000000000000000000000000000000000000", "Measurements file verified!"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
{
|
||||
name: "successful_retrieval_custom_filename",
|
||||
args: []string{"custom_ima_file.txt"},
|
||||
connectErr: nil,
|
||||
mockIMAData: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
mockError: nil,
|
||||
expectedFilename: "custom_ima_file.txt",
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "custom_ima_file.txt", "Measurements file verified!"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
{
|
||||
name: "connection_error",
|
||||
args: []string{},
|
||||
connectErr: fmt.Errorf("connection failed"),
|
||||
expectedError: []string{"Failed to connect to agent: connection failed ❌"},
|
||||
},
|
||||
{
|
||||
name: "file_creation_error",
|
||||
args: []string{"/invalid/path/file.txt"},
|
||||
connectErr: nil,
|
||||
fileCreationError: true,
|
||||
expectedError: []string{"Error creating imaMeasurements file:"},
|
||||
},
|
||||
{
|
||||
name: "sdk_error",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockError: fmt.Errorf("SDK communication failed"),
|
||||
expectedError: []string{"Error retrieving Linux IMA measurements file: SDK communication failed ❌"},
|
||||
},
|
||||
{
|
||||
name: "verification_failure_wrong_pcr",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockIMAData: "10 9999999999999999999999999999999999999999 ima-ng sha1:0000000000000000000000000000000000000000 /usr/bin/test",
|
||||
mockError: nil,
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "Linux IMA measurements file retrieved and saved successfully"},
|
||||
expectedError: []string{"Measurements file not verified ❌"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
{
|
||||
name: "empty_measurements_file",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockIMAData: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
mockError: nil,
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "Linux IMA measurements file retrieved and saved successfully", "Measurements file verified!"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
{
|
||||
name: "measurements_with_non_pcr10_entries",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockIMAData: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
mockError: nil,
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "Linux IMA measurements file retrieved and saved successfully", "Measurements file verified!"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
{
|
||||
name: "measurements_with_zero_digest_replacement",
|
||||
args: []string{},
|
||||
connectErr: nil,
|
||||
mockIMAData: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
mockError: nil,
|
||||
expectedOutput: []string{"⏳ Retrieving computation Linux IMA measurements file", "Linux IMA measurements file retrieved and saved successfully", "Measurements file verified!"},
|
||||
shouldCreateFile: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockSDK := new(mocks.SDK)
|
||||
|
||||
cli := &CLI{
|
||||
agentSDK: mockSDK,
|
||||
connectErr: tc.connectErr,
|
||||
}
|
||||
|
||||
if tc.connectErr == nil && !tc.fileCreationError {
|
||||
mockSDK.On("IMAMeasurements", mock.Anything, mock.Anything).Return([]byte(tc.mockIMAData), tc.mockError)
|
||||
}
|
||||
|
||||
cmd := cli.NewIMAMeasurementsCmd()
|
||||
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
cmd.SetErr(&output)
|
||||
|
||||
expectedFilename := tc.expectedFilename
|
||||
if expectedFilename == "" {
|
||||
if len(tc.args) > 0 {
|
||||
expectedFilename = tc.args[0]
|
||||
} else {
|
||||
expectedFilename = imaMeasurementsFilename
|
||||
}
|
||||
}
|
||||
|
||||
if tc.setupCustomFile != nil {
|
||||
err := tc.setupCustomFile(expectedFilename)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err, "Command execution failed")
|
||||
|
||||
outputStr := output.String()
|
||||
|
||||
for _, expectedMsg := range tc.expectedOutput {
|
||||
assert.Contains(t, outputStr, expectedMsg, "Expected output message not found")
|
||||
}
|
||||
|
||||
for _, expectedErr := range tc.expectedError {
|
||||
assert.Contains(t, outputStr, expectedErr, "Expected error message not found")
|
||||
}
|
||||
|
||||
if tc.shouldCreateFile && tc.connectErr == nil && !tc.fileCreationError && tc.mockError == nil {
|
||||
if _, err := os.Stat(expectedFilename); err == nil {
|
||||
os.Remove(expectedFilename)
|
||||
}
|
||||
}
|
||||
|
||||
if tc.connectErr == nil && !tc.fileCreationError {
|
||||
mockSDK.AssertExpectations(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+30
-13
@@ -4,6 +4,7 @@ package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -15,7 +16,9 @@ const (
|
||||
serverCA = "server-ca"
|
||||
clientKey = "client-key"
|
||||
clientCrt = "client-crt"
|
||||
caUrl = "ca-url"
|
||||
logLevel = "log-level"
|
||||
ttlFlag = "ttl"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,7 +26,9 @@ var (
|
||||
agentCVMServerCA string
|
||||
agentCVMClientKey string
|
||||
agentCVMClientCrt string
|
||||
agentCVMCaUrl string
|
||||
agentLogLevel string
|
||||
ttl time.Duration
|
||||
)
|
||||
|
||||
func (c *CLI) NewCreateVMCmd() *cobra.Command {
|
||||
@@ -33,9 +38,11 @@ func (c *CLI) NewCreateVMCmd() *cobra.Command {
|
||||
Example: `create-vm`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := c.InitializeManagerClient(cmd); err != nil {
|
||||
printError(cmd, "Failed to connect to manager: %v ❌ ", c.connectErr)
|
||||
return
|
||||
if c.managerClient == nil || c.connectErr != nil {
|
||||
if err := c.InitializeManagerClient(cmd); err != nil {
|
||||
printError(cmd, "Failed to connect to manager: %v ❌ ", c.connectErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
@@ -47,6 +54,11 @@ func (c *CLI) NewCreateVMCmd() *cobra.Command {
|
||||
|
||||
createReq.AgentCvmServerUrl = agentCVMServerUrl
|
||||
createReq.AgentLogLevel = agentLogLevel
|
||||
createReq.AgentCvmCaUrl = agentCVMCaUrl
|
||||
|
||||
if ttl > 0 {
|
||||
createReq.Ttl = ttl.String()
|
||||
}
|
||||
|
||||
cmd.Println("🔗 Creating a new virtual machine")
|
||||
|
||||
@@ -56,7 +68,7 @@ func (c *CLI) NewCreateVMCmd() *cobra.Command {
|
||||
return
|
||||
}
|
||||
|
||||
cmd.Println(color.New(color.FgGreen).Sprintf("✅ Virtual machine created successfully with id %s and port %s", res.SvmId, res.ForwardedPort))
|
||||
cmd.Println(color.New(color.FgGreen).Sprintf("✅ Virtual machine created successfully with id %s and port %s", res.CvmId, res.ForwardedPort))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,7 +76,13 @@ func (c *CLI) NewCreateVMCmd() *cobra.Command {
|
||||
cmd.Flags().StringVar(&agentCVMServerCA, serverCA, "", "CVM server CA")
|
||||
cmd.Flags().StringVar(&agentCVMClientKey, clientKey, "", "CVM client key")
|
||||
cmd.Flags().StringVar(&agentCVMClientCrt, clientCrt, "", "CVM client crt")
|
||||
cmd.Flags().StringVar(&agentCVMCaUrl, caUrl, "", "CVM CA service URL")
|
||||
cmd.Flags().StringVar(&agentLogLevel, logLevel, "", "Agent Log level")
|
||||
cmd.Flags().DurationVar(&ttl, ttlFlag, 0, "TTL for the VM")
|
||||
if err := cmd.MarkFlagRequired(serverURL); err != nil {
|
||||
printError(cmd, "Error marking flag as required: %v ❌ ", err)
|
||||
return cmd
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -73,21 +91,20 @@ func (c *CLI) NewRemoveVMCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "remove-vm",
|
||||
Short: "Remove a virtual machine",
|
||||
Example: `remove-vm <svm_id>`,
|
||||
Example: `remove-vm <cvm_id>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := c.InitializeManagerClient(cmd); err == nil {
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
if c.connectErr != nil {
|
||||
printError(cmd, "Failed to connect to manager: %v ❌ ", c.connectErr)
|
||||
return
|
||||
if c.managerClient == nil || c.connectErr != nil {
|
||||
if err := c.InitializeManagerClient(cmd); err != nil {
|
||||
printError(cmd, "Failed to connect to manager: %v ❌ ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
cmd.Println("🔗 Removing virtual machine")
|
||||
|
||||
_, err := c.managerClient.RemoveVm(cmd.Context(), &manager.RemoveReq{SvmId: args[0]})
|
||||
_, err := c.managerClient.RemoveVm(cmd.Context(), &manager.RemoveReq{CvmId: args[0]})
|
||||
if err != nil {
|
||||
printError(cmd, "Error removing virtual machine: %v ❌ ", err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/manager"
|
||||
"github.com/ultravioletrs/cocos/manager/mocks"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
func TestCLI_NewCreateVMCmd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupMock func(*mocks.ManagerServiceClient)
|
||||
setupCLI func(*CLI)
|
||||
setupFiles func(string) error
|
||||
flags map[string]string
|
||||
expectedOutput string
|
||||
expectedError string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "successful VM creation with all flags",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
m.On("CreateVm", mock.Anything, mock.MatchedBy(func(req *manager.CreateReq) bool {
|
||||
return req.AgentCvmServerUrl == "https://server.com" &&
|
||||
req.AgentLogLevel == "debug" &&
|
||||
req.AgentCvmCaUrl == "https://ca.com" &&
|
||||
req.Ttl == "1h0m0s" &&
|
||||
string(req.AgentCvmServerCaCert) == "ca-cert-content" &&
|
||||
string(req.AgentCvmClientKey) == "client-key-content" &&
|
||||
string(req.AgentCvmClientCert) == "client-cert-content"
|
||||
})).Return(&manager.CreateRes{
|
||||
CvmId: "vm-123",
|
||||
ForwardedPort: "8080",
|
||||
}, nil)
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
files := map[string]string{
|
||||
"server-ca.pem": "ca-cert-content",
|
||||
"client-key.pem": "client-key-content",
|
||||
"client-crt.pem": "client-cert-content",
|
||||
}
|
||||
for filename, content := range files {
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, filename), []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
flags: map[string]string{
|
||||
"server-url": "https://server.com",
|
||||
"server-ca": "server-ca.pem",
|
||||
"client-key": "client-key.pem",
|
||||
"client-crt": "client-crt.pem",
|
||||
"ca-url": "https://ca.com",
|
||||
"log-level": "debug",
|
||||
"ttl": "1h",
|
||||
},
|
||||
expectedOutput: "✅ Virtual machine created successfully with id vm-123 and port 8080",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "successful VM creation with minimal flags",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
m.On("CreateVm", mock.Anything, mock.MatchedBy(func(req *manager.CreateReq) bool {
|
||||
return req.AgentCvmServerUrl == "https://server.com" &&
|
||||
req.AgentLogLevel == "" &&
|
||||
req.AgentCvmCaUrl == "" &&
|
||||
req.Ttl == "" &&
|
||||
len(req.AgentCvmServerCaCert) == 0 &&
|
||||
len(req.AgentCvmClientKey) == 0 &&
|
||||
len(req.AgentCvmClientCert) == 0
|
||||
})).Return(&manager.CreateRes{
|
||||
CvmId: "vm-456",
|
||||
ForwardedPort: "9090",
|
||||
}, nil)
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil // No files needed for minimal test
|
||||
},
|
||||
flags: map[string]string{
|
||||
"server-url": "https://server.com",
|
||||
},
|
||||
expectedOutput: "✅ Virtual machine created successfully with id vm-456 and port 9090",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "manager client initialization failure",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as initialization fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
cli.connectErr = errors.New("connection failed")
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil
|
||||
},
|
||||
flags: map[string]string{
|
||||
"server-url": "https://server.com",
|
||||
},
|
||||
expectedError: "Failed to connect to manager: failed to connect to grpc server : failed to exit idle mode: passthrough: received empty target in Build() ❌",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "certificate loading failure",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as cert loading fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil // Don't create the cert file
|
||||
},
|
||||
flags: map[string]string{
|
||||
"server-url": "https://server.com",
|
||||
"server-ca": "nonexistent-ca.pem",
|
||||
},
|
||||
expectedError: "Error loading certs:",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "CreateVm API call failure",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
m.On("CreateVm", mock.Anything, mock.Anything).Return(nil, errors.New("API error"))
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil
|
||||
},
|
||||
flags: map[string]string{
|
||||
"server-url": "https://server.com",
|
||||
},
|
||||
expectedError: "Error creating virtual machine: API error ❌",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "missing required server-url flag",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as command validation fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil
|
||||
},
|
||||
flags: map[string]string{}, // No server-url flag
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "cli-test-")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
oldDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
err = os.Chdir(tmpDir)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err := os.Chdir(oldDir)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
err = tt.setupFiles(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockClient := new(mocks.ManagerServiceClient)
|
||||
tt.setupMock(mockClient)
|
||||
|
||||
mockCLI := &CLI{
|
||||
managerClient: mockClient,
|
||||
}
|
||||
|
||||
tt.setupCLI(mockCLI)
|
||||
|
||||
cmd := mockCLI.NewCreateVMCmd()
|
||||
|
||||
for flag, value := range tt.flags {
|
||||
err := cmd.Flags().Set(flag, value)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err = cmd.Execute()
|
||||
|
||||
if tt.expectError {
|
||||
if tt.expectedError != "" {
|
||||
assert.Contains(t, buf.String(), tt.expectedError)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tt.expectedOutput != "" {
|
||||
assert.Contains(t, buf.String(), tt.expectedOutput)
|
||||
}
|
||||
}
|
||||
|
||||
mockClient.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLI_NewRemoveVMCmd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupMock func(*mocks.ManagerServiceClient)
|
||||
setupCLI func(*CLI)
|
||||
args []string
|
||||
expectedOutput string
|
||||
expectedError string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "successful VM removal",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
m.On("RemoveVm", mock.Anything, &manager.RemoveReq{
|
||||
CvmId: "vm-123",
|
||||
}).Return(&emptypb.Empty{}, nil)
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
args: []string{"vm-123"},
|
||||
expectedOutput: "✅ Virtual machine removed successfully",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "manager client initialization failure",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as initialization fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
cli.connectErr = errors.New("connection failed")
|
||||
},
|
||||
args: []string{"vm-123"},
|
||||
expectedError: "Failed to connect to manager: failed to connect to grpc server : failed to exit idle mode: passthrough: received empty target in Build() ❌",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "RemoveVm API call failure",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
m.On("RemoveVm", mock.Anything, &manager.RemoveReq{
|
||||
CvmId: "vm-456",
|
||||
}).Return(nil, errors.New("removal failed"))
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
args: []string{"vm-456"},
|
||||
expectedError: "Error removing virtual machine: removal failed ❌",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "missing VM ID argument",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as command validation fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
args: []string{}, // No VM ID provided
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "too many arguments",
|
||||
setupMock: func(m *mocks.ManagerServiceClient) {
|
||||
// No expectations set as command validation fails
|
||||
},
|
||||
setupCLI: func(cli *CLI) {
|
||||
},
|
||||
args: []string{"vm-123", "extra-arg"},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockClient := new(mocks.ManagerServiceClient)
|
||||
tt.setupMock(mockClient)
|
||||
|
||||
mockCLI := &CLI{
|
||||
managerClient: mockClient,
|
||||
}
|
||||
tt.setupCLI(mockCLI)
|
||||
|
||||
cmd := mockCLI.NewRemoveVMCmd()
|
||||
|
||||
cmd.SetArgs(tt.args)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.SetOut(&buf)
|
||||
cmd.SetErr(&buf)
|
||||
|
||||
err := cmd.Execute()
|
||||
|
||||
if tt.expectError {
|
||||
if tt.expectedError != "" {
|
||||
assert.Contains(t, buf.String(), tt.expectedError)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
if tt.expectedOutput != "" {
|
||||
assert.Contains(t, buf.String(), tt.expectedOutput)
|
||||
}
|
||||
}
|
||||
|
||||
mockClient.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileReader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFile func(string) (string, error)
|
||||
path string
|
||||
expectedResult []byte
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "successful file read",
|
||||
setupFile: func(tmpDir string) (string, error) {
|
||||
filePath := filepath.Join(tmpDir, "test.txt")
|
||||
err := os.WriteFile(filePath, []byte("test content"), 0o644)
|
||||
return filePath, err
|
||||
},
|
||||
expectedResult: []byte("test content"),
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "empty path returns nil",
|
||||
setupFile: func(tmpDir string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
path: "",
|
||||
expectedResult: nil,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "nonexistent file returns error",
|
||||
setupFile: func(tmpDir string) (string, error) {
|
||||
return filepath.Join(tmpDir, "nonexistent.txt"), nil
|
||||
},
|
||||
expectedResult: nil,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "fileReader-test-")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
filePath, err := tt.setupFile(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.path != "" {
|
||||
filePath = tt.path
|
||||
}
|
||||
|
||||
result, err := fileReader(filePath)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCerts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFiles func(string) error
|
||||
setupGlobal func(string)
|
||||
expectError bool
|
||||
validate func(*testing.T, *manager.CreateReq)
|
||||
}{
|
||||
{
|
||||
name: "successful cert loading with all files",
|
||||
setupFiles: func(tmpDir string) error {
|
||||
files := map[string]string{
|
||||
"client.key": "client-key-content",
|
||||
"client.crt": "client-cert-content",
|
||||
"server.ca": "server-ca-content",
|
||||
}
|
||||
for filename, content := range files {
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, filename), []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
setupGlobal: func(tmpDir string) {
|
||||
agentCVMClientKey = filepath.Join(tmpDir, "client.key")
|
||||
agentCVMClientCrt = filepath.Join(tmpDir, "client.crt")
|
||||
agentCVMServerCA = filepath.Join(tmpDir, "server.ca")
|
||||
},
|
||||
expectError: false,
|
||||
validate: func(t *testing.T, req *manager.CreateReq) {
|
||||
assert.Equal(t, []byte("client-key-content"), req.AgentCvmClientKey)
|
||||
assert.Equal(t, []byte("client-cert-content"), req.AgentCvmClientCert)
|
||||
assert.Equal(t, []byte("server-ca-content"), req.AgentCvmServerCaCert)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "successful cert loading with empty paths",
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil
|
||||
},
|
||||
setupGlobal: func(tmpDir string) {
|
||||
agentCVMClientKey = ""
|
||||
agentCVMClientCrt = ""
|
||||
agentCVMServerCA = ""
|
||||
},
|
||||
expectError: false,
|
||||
validate: func(t *testing.T, req *manager.CreateReq) {
|
||||
assert.Nil(t, req.AgentCvmClientKey)
|
||||
assert.Nil(t, req.AgentCvmClientCert)
|
||||
assert.Nil(t, req.AgentCvmServerCaCert)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "client key file read error",
|
||||
setupFiles: func(tmpDir string) error {
|
||||
return nil // Don't create client key file
|
||||
},
|
||||
setupGlobal: func(tmpDir string) {
|
||||
agentCVMClientKey = filepath.Join(tmpDir, "nonexistent.key")
|
||||
agentCVMClientCrt = ""
|
||||
agentCVMServerCA = ""
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "client cert file read error",
|
||||
setupFiles: func(tmpDir string) error {
|
||||
// Create client key but not cert
|
||||
return os.WriteFile(filepath.Join(tmpDir, "client.key"), []byte("key-content"), 0o644)
|
||||
},
|
||||
setupGlobal: func(tmpDir string) {
|
||||
agentCVMClientKey = filepath.Join(tmpDir, "client.key")
|
||||
agentCVMClientCrt = filepath.Join(tmpDir, "nonexistent.crt")
|
||||
agentCVMServerCA = ""
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "server CA file read error",
|
||||
setupFiles: func(tmpDir string) error {
|
||||
files := map[string]string{
|
||||
"client.key": "client-key-content",
|
||||
"client.crt": "client-cert-content",
|
||||
}
|
||||
for filename, content := range files {
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, filename), []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
setupGlobal: func(tmpDir string) {
|
||||
agentCVMClientKey = filepath.Join(tmpDir, "client.key")
|
||||
agentCVMClientCrt = filepath.Join(tmpDir, "client.crt")
|
||||
agentCVMServerCA = filepath.Join(tmpDir, "nonexistent.ca")
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "loadCerts-test-")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
err = tt.setupFiles(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Store original global variables
|
||||
origClientKey := agentCVMClientKey
|
||||
origClientCrt := agentCVMClientCrt
|
||||
origServerCA := agentCVMServerCA
|
||||
|
||||
// Setup global variables for test
|
||||
tt.setupGlobal(tmpDir)
|
||||
|
||||
// Restore original values after test
|
||||
defer func() {
|
||||
agentCVMClientKey = origClientKey
|
||||
agentCVMClientCrt = origClientCrt
|
||||
agentCVMServerCA = origServerCA
|
||||
}()
|
||||
|
||||
result, err := loadCerts()
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
if tt.validate != nil {
|
||||
tt.validate(t, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandCreation(t *testing.T) {
|
||||
cli := &CLI{}
|
||||
|
||||
t.Run("create-vm command creation", func(t *testing.T) {
|
||||
cmd := cli.NewCreateVMCmd()
|
||||
assert.NotNil(t, cmd)
|
||||
assert.Equal(t, "create-vm", cmd.Use)
|
||||
assert.Equal(t, "Create a new virtual machine", cmd.Short)
|
||||
|
||||
// Check that required flags are set
|
||||
flag := cmd.Flags().Lookup("server-url")
|
||||
assert.NotNil(t, flag)
|
||||
// Note: We can't easily test MarkFlagRequired in unit tests
|
||||
})
|
||||
|
||||
t.Run("remove-vm command creation", func(t *testing.T) {
|
||||
cmd := cli.NewRemoveVMCmd()
|
||||
assert.NotNil(t, cmd)
|
||||
assert.Equal(t, "remove-vm", cmd.Use)
|
||||
assert.Equal(t, "Remove a virtual machine", cmd.Short)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTTLHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ttlInput string
|
||||
expectedTTL time.Duration
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid duration",
|
||||
ttlInput: "1h30m",
|
||||
expectedTTL: time.Hour + 30*time.Minute,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "zero duration",
|
||||
ttlInput: "0",
|
||||
expectedTTL: 0,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
ttlInput: "",
|
||||
expectedTTL: 0,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockCLI := &CLI{
|
||||
managerClient: new(mocks.ManagerServiceClient),
|
||||
}
|
||||
|
||||
cmd := mockCLI.NewCreateVMCmd()
|
||||
|
||||
if tt.ttlInput != "" {
|
||||
err := cmd.Flags().Set("ttl", tt.ttlInput)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedTTL, ttl)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ultravioletrs/cocos/manager"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/igvmmeasure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/cmdconfig"
|
||||
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
"github.com/ultravioletrs/cocos/pkg/clients/grpc/agent"
|
||||
managergrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc/manager"
|
||||
@@ -23,10 +23,10 @@ type CLI struct {
|
||||
client grpc.Client
|
||||
managerClient manager.ManagerServiceClient
|
||||
connectErr error
|
||||
measurement igvmmeasure.MeasurementProvider
|
||||
measurement cmdconfig.MeasurementProvider
|
||||
}
|
||||
|
||||
func New(agentConfig grpc.AgentClientConfig, managerConfig grpc.ManagerClientConfig, measurement igvmmeasure.MeasurementProvider) *CLI {
|
||||
func New(agentConfig grpc.AgentClientConfig, managerConfig grpc.ManagerClientConfig, measurement cmdconfig.MeasurementProvider) *CLI {
|
||||
return &CLI{
|
||||
agentConfig: agentConfig,
|
||||
managerConfig: managerConfig,
|
||||
@@ -40,7 +40,7 @@ func (c *CLI) InitializeAgentSDK(cmd *cobra.Command) error {
|
||||
c.connectErr = err
|
||||
return err
|
||||
}
|
||||
cmd.Println("🔗 Connected to agent using ", agentGRPCClient.Secure())
|
||||
cmd.Println("🔗 Connected to agent ", agentGRPCClient.Secure())
|
||||
c.client = agentGRPCClient
|
||||
|
||||
c.agentSDK = sdk.NewAgentSDK(agentClient)
|
||||
@@ -62,5 +62,7 @@ func (c *CLI) InitializeManagerClient(cmd *cobra.Command) error {
|
||||
}
|
||||
|
||||
func (c *CLI) Close() {
|
||||
c.client.Close()
|
||||
if c.client != nil {
|
||||
c.client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
+135
-36
@@ -4,7 +4,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
@@ -13,11 +16,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
mglog "github.com/absmach/magistrala/logger"
|
||||
"github.com/absmach/magistrala/pkg/prometheus"
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/absmach/supermq/pkg/prometheus"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/google/go-sev-guest/client"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
"github.com/ultravioletrs/cocos/agent/api"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
@@ -25,8 +26,10 @@ import (
|
||||
"github.com/ultravioletrs/cocos/agent/cvms/server"
|
||||
"github.com/ultravioletrs/cocos/agent/events"
|
||||
agentlogger "github.com/ultravioletrs/cocos/internal/logger"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/azure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/tdx"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
pkggrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
cvmsgrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc/cvm"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -41,8 +44,15 @@ const (
|
||||
)
|
||||
|
||||
type config struct {
|
||||
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"debug"`
|
||||
Vmpl int `env:"AGENT_VMPL" envDefault:"2"`
|
||||
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"debug"`
|
||||
Vmpl int `env:"AGENT_VMPL" envDefault:"2"`
|
||||
AgentGrpcHost string `env:"AGENT_GRPC_HOST" envDefault:"0.0.0.0"`
|
||||
CAUrl string `env:"AGENT_CVM_CA_URL" envDefault:""`
|
||||
CVMId string `env:"AGENT_CVM_ID" envDefault:""`
|
||||
AgentMaaURL string `env:"AGENT_MAA_URL" envDefault:"https://sharedeus2.eus2.attest.azure.net"`
|
||||
AgentOSBuild string `env:"AGENT_OS_BUILD" envDefault:"UVC"`
|
||||
AgentOSDistro string `env:"AGENT_OS_DISTRO" envDefault:"UVC"`
|
||||
AgentOSType string `env:"AGENT_OS_TYPE" envDefault:"UVC"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -76,20 +86,29 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
var qp client.LeveledQuoteProvider
|
||||
var provider attestation.Provider
|
||||
ccPlatform := attestation.CCPlatform()
|
||||
|
||||
if !sevGuesDeviceExists() {
|
||||
logger.Info("SEV-SNP device not found")
|
||||
qpMock := new(mocks.LeveledQuoteProvider)
|
||||
qpMock.On("GetRawQuoteAtLevel", mock.Anything, mock.Anything).Return([]uint8{}, errors.New("SEV-SNP device not found"))
|
||||
qp = qpMock
|
||||
} else {
|
||||
qp, err = quoteprovider.GetLeveledQuoteProvider()
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create quote provider %s", err.Error()))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
azureConfig := azure.NewEnvConfigFromAgent(
|
||||
cfg.AgentOSBuild,
|
||||
cfg.AgentOSType,
|
||||
cfg.AgentOSDistro,
|
||||
cfg.AgentMaaURL,
|
||||
)
|
||||
azure.InitializeDefaultMAAVars(azureConfig)
|
||||
|
||||
switch ccPlatform {
|
||||
case attestation.SNP:
|
||||
provider = vtpm.NewProvider(false, uint(cfg.Vmpl))
|
||||
case attestation.SNPvTPM:
|
||||
provider = vtpm.NewProvider(true, uint(cfg.Vmpl))
|
||||
case attestation.Azure:
|
||||
provider = azure.NewProvider()
|
||||
case attestation.TDX:
|
||||
provider = tdx.NewProvider()
|
||||
case attestation.NoCC:
|
||||
logger.Info("TEE device not found")
|
||||
provider = &attestation.EmptyProvider{}
|
||||
}
|
||||
|
||||
cvmGrpcConfig := pkggrpc.CVMClientConfig{}
|
||||
@@ -107,14 +126,19 @@ func main() {
|
||||
}
|
||||
defer cvmGRPCClient.Close()
|
||||
|
||||
reconnectFn := func(ctx context.Context) (cvms.Service_ProcessClient, error) {
|
||||
_, newClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig)
|
||||
reconnectFn := func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) {
|
||||
grpcClient, newClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
// Don't defer close here as we want to keep the connection open
|
||||
|
||||
return newClient.Process(ctx)
|
||||
pc, err := newClient.Process(ctx)
|
||||
if err != nil {
|
||||
grpcClient.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return grpcClient, pc, nil
|
||||
}
|
||||
|
||||
pc, err := cvmsClient.Process(ctx)
|
||||
@@ -130,7 +154,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
svc := newService(ctx, logger, eventSvc, qp, cfg.Vmpl)
|
||||
svc := newService(ctx, logger, eventSvc, provider, cfg.Vmpl)
|
||||
|
||||
if err := os.MkdirAll(storageDir, 0o755); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create storage directory: %s", err))
|
||||
@@ -138,7 +162,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
mc, err := cvmsapi.NewClient(pc, svc, eventsLogsQueue, logger, server.NewServer(logger, svc), storageDir, reconnectFn)
|
||||
mc, err := cvmsapi.NewClient(pc, svc, eventsLogsQueue, logger, server.NewServer(logger, svc, cfg.AgentGrpcHost, cfg.CAUrl, cfg.CVMId), storageDir, reconnectFn, cvmGRPCClient)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
@@ -164,13 +188,46 @@ func main() {
|
||||
return mc.Process(ctx, cancel)
|
||||
})
|
||||
|
||||
attest, certSerialNumber, err := attestationFromCert(ctx, cvmGrpcConfig.ClientCert, svc)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to get attestation: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if ccPlatform == attestation.Azure {
|
||||
azureAttestationResult, azureCertSerialNumber, err := azureAttestationFromCert(ctx, cvmGrpcConfig.ClientCert, svc)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to get attestation: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
eventsLogsQueue <- &cvms.ClientStreamMessage{
|
||||
Message: &cvms.ClientStreamMessage_AzureAttestationResult{
|
||||
AzureAttestationResult: &cvms.AzureAttestationResponse{
|
||||
File: azureAttestationResult,
|
||||
CertSerialNumber: azureCertSerialNumber,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
eventsLogsQueue <- &cvms.ClientStreamMessage{
|
||||
Message: &cvms.ClientStreamMessage_VTPMattestationReport{
|
||||
VTPMattestationReport: &cvms.AttestationResponse{
|
||||
File: attest,
|
||||
CertSerialNumber: certSerialNumber,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
logger.Error(fmt.Sprintf("%s service terminated: %s", svcName, err))
|
||||
}
|
||||
}
|
||||
|
||||
func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Service, qp client.LeveledQuoteProvider, vmpl int) agent.Service {
|
||||
svc := agent.New(ctx, logger, eventSvc, qp, vmpl)
|
||||
func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Service, provider attestation.Provider, vmpl int) agent.Service {
|
||||
svc := agent.New(ctx, logger, eventSvc, provider, vmpl)
|
||||
|
||||
svc = api.LoggingMiddleware(svc, logger)
|
||||
counter, latency := prometheus.MakeMetrics(svcName, "api")
|
||||
@@ -179,11 +236,53 @@ func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Servic
|
||||
return svc
|
||||
}
|
||||
|
||||
func sevGuesDeviceExists() bool {
|
||||
d, err := client.OpenDevice()
|
||||
if err != nil {
|
||||
return false
|
||||
func attestationFromCert(ctx context.Context, certFilePath string, svc agent.Service) ([]byte, string, error) {
|
||||
if certFilePath == "" {
|
||||
return nil, "", nil
|
||||
}
|
||||
d.Close()
|
||||
return true
|
||||
|
||||
certFile, err := os.ReadFile(certFilePath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
certPem, _ := pem.Decode(certFile)
|
||||
certx509, err := x509.ParseCertificate(certPem.Bytes)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
nonceSNP := sha512.Sum512(certFile)
|
||||
nonceVTPM := sha256.Sum256(certFile)
|
||||
attest, err := svc.Attestation(ctx, nonceSNP, nonceVTPM, attestation.SNPvTPM)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return attest, certx509.SerialNumber.String(), nil
|
||||
}
|
||||
|
||||
func azureAttestationFromCert(ctx context.Context, certFilePath string, svc agent.Service) ([]byte, string, error) {
|
||||
if certFilePath == "" {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
certFile, err := os.ReadFile(certFilePath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
certPem, _ := pem.Decode(certFile)
|
||||
certx509, err := x509.ParseCertificate(certPem.Bytes)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
nonceAzure := sha256.Sum256(certFile)
|
||||
attestation, err := svc.AttestationResult(ctx, nonceAzure, attestation.AzureToken)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return attestation, certx509.SerialNumber.String(), nil
|
||||
}
|
||||
|
||||
+8
-2
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/ultravioletrs/cocos/cli"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/igvmmeasure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/cmdconfig"
|
||||
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
cmd "github.com/virtee/sev-snp-measure-go/sevsnpmeasure/cmd"
|
||||
)
|
||||
@@ -108,7 +108,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
measurement, err := igvmmeasure.NewIgvmMeasurement(cfg.IgvmBinaryPath, os.Stderr, os.Stdout)
|
||||
options := cmdconfig.IgvmMeasureOptions
|
||||
measurement, err := cmdconfig.NewCmdConfig(cfg.IgvmBinaryPath, options, os.Stderr)
|
||||
if err != nil {
|
||||
message := color.New(color.FgRed).Sprintf("failed to initialize measurement: %s", err) // Use %s instead of %w
|
||||
rootCmd.Println(message)
|
||||
@@ -138,6 +139,7 @@ func main() {
|
||||
rootCmd.AddCommand(cliSVC.NewCABundleCmd(directoryCachePath))
|
||||
rootCmd.AddCommand(cliSVC.NewCreateVMCmd())
|
||||
rootCmd.AddCommand(cliSVC.NewRemoveVMCmd())
|
||||
rootCmd.AddCommand(cliSVC.NewIMAMeasurementsCmd())
|
||||
|
||||
// Attestation commands
|
||||
attestationCmd.AddCommand(cliSVC.NewGetAttestationCmd())
|
||||
@@ -159,6 +161,10 @@ func main() {
|
||||
// Attestation Policy commands
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewAddMeasurementCmd())
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewAddHostDataCmd())
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewGCPAttestationPolicy())
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewDownloadGCPOvmfFile())
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewAzureAttestationPolicy())
|
||||
attestationPolicyCmd.AddCommand(cliSVC.NewExtendWithManifestCmd())
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
logErrorCmd(*rootCmd, err)
|
||||
|
||||
+54
-12
@@ -12,16 +12,20 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
mglog "github.com/absmach/magistrala/logger"
|
||||
"github.com/absmach/magistrala/pkg/jaeger"
|
||||
"github.com/absmach/magistrala/pkg/prometheus"
|
||||
"github.com/absmach/magistrala/pkg/uuid"
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/prometheus"
|
||||
smqserver "github.com/absmach/supermq/pkg/server"
|
||||
httpserver "github.com/absmach/supermq/pkg/server/http"
|
||||
"github.com/absmach/supermq/pkg/uuid"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/ultravioletrs/cocos/internal/server"
|
||||
grpcserver "github.com/ultravioletrs/cocos/internal/server/grpc"
|
||||
"github.com/ultravioletrs/cocos/manager"
|
||||
"github.com/ultravioletrs/cocos/manager/api"
|
||||
managergrpc "github.com/ultravioletrs/cocos/manager/api/grpc"
|
||||
"github.com/ultravioletrs/cocos/manager/api/http"
|
||||
"github.com/ultravioletrs/cocos/manager/qemu"
|
||||
"github.com/ultravioletrs/cocos/manager/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -33,8 +37,10 @@ import (
|
||||
const (
|
||||
svcName = "manager"
|
||||
envPrefixGRPC = "MANAGER_GRPC_"
|
||||
envPrefixHTTP = "MANAGER_HTTP_"
|
||||
envPrefixQemu = "MANAGER_QEMU_"
|
||||
clientBufferSize = 100
|
||||
defSvcHTTPPort = "7003"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
@@ -42,8 +48,11 @@ type config struct {
|
||||
JaegerURL url.URL `env:"COCOS_JAEGER_URL" envDefault:"http://localhost:4318"`
|
||||
TraceRatio float64 `env:"COCOS_JAEGER_TRACE_RATIO" envDefault:"1.0"`
|
||||
InstanceID string `env:"MANAGER_INSTANCE_ID" envDefault:""`
|
||||
AttestationPolicyBinary string `env:"MANAGER_ATTESTATION_POLICY_BINARY" envDefault:"../../build"`
|
||||
AttestationPolicyBinary string `env:"MANAGER_ATTESTATION_POLICY_BINARY" envDefault:"../../build/attestation_policy"`
|
||||
IgvmMeasureBinary string `env:"MANAGER_IGVMMEASURE_BINARY" envDefault:"../../build/igvmmeasure"`
|
||||
PcrValues string `env:"MANAGER_PCR_VALUES" envDefault:""`
|
||||
EosVersion string `env:"MANAGER_EOS_VERSION" envDefault:""`
|
||||
MaxVMs int `env:"MANAGER_MAX_VMS" envDefault:"10"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -82,8 +91,22 @@ func main() {
|
||||
}()
|
||||
tracer := tp.Tracer(svcName)
|
||||
|
||||
qemuCfg := qemu.Config{}
|
||||
if err := env.ParseWithOptions(&qemuCfg, env.Options{Prefix: envPrefixQemu}); err != nil {
|
||||
qemuCfg, err := qemu.NewConfig()
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create config: %v", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if qemuCfg.EnableTDX {
|
||||
logger.Info("Manager started with TDX enabled")
|
||||
} else if qemuCfg.EnableSEVSNP {
|
||||
logger.Info("Manager started with SEV-SNP enabled")
|
||||
} else {
|
||||
logger.Info("Manager started without confidential computing support")
|
||||
}
|
||||
|
||||
if err := env.ParseWithOptions(qemuCfg, env.Options{Prefix: envPrefixQemu}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load QEMU configuration: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -98,26 +121,45 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
svc, err := newService(logger, tracer, qemuCfg, cfg.AttestationPolicyBinary, cfg.EosVersion)
|
||||
httpServerConfig := smqserver.Config{Port: defSvcHTTPPort}
|
||||
if err := env.ParseWithOptions(&httpServerConfig, env.Options{Prefix: envPrefixHTTP}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s gRPC server configuration : %s", svcName, err))
|
||||
}
|
||||
|
||||
svc, err := newService(logger, tracer, *qemuCfg, cfg.AttestationPolicyBinary, cfg.IgvmMeasureBinary, cfg.PcrValues, cfg.EosVersion, cfg.MaxVMs)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := svc.Shutdown(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
registerManagerServiceServer := func(srv *grpc.Server) {
|
||||
reflection.Register(srv)
|
||||
manager.RegisterManagerServiceServer(srv, managergrpc.NewServer(svc))
|
||||
}
|
||||
|
||||
gs := grpcserver.New(ctx, cancel, svcName, managerGRPCConfig, registerManagerServiceServer, logger, nil, nil)
|
||||
gs := grpcserver.New(ctx, cancel, svcName, managerGRPCConfig, registerManagerServiceServer, logger, nil, "", "")
|
||||
|
||||
hs := httpserver.NewServer(ctx, cancel, svcName, httpServerConfig, http.MakeHandler(chi.NewMux(), svcName, cfg.InstanceID), logger)
|
||||
|
||||
g.Go(func() error {
|
||||
return gs.Start()
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
return server.StopHandler(ctx, cancel, logger, svcName, gs)
|
||||
return hs.Start()
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
return server.StopHandler(ctx, cancel, logger, svcName, gs, hs)
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
@@ -125,8 +167,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(logger *slog.Logger, tracer trace.Tracer, qemuCfg qemu.Config, attestationPolicyPath string, eosVersion string) (manager.Service, error) {
|
||||
svc, err := manager.New(qemuCfg, attestationPolicyPath, logger, qemu.NewVM, eosVersion)
|
||||
func newService(logger *slog.Logger, tracer trace.Tracer, qemuCfg qemu.Config, attestationPolicyPath string, igvmMeasurementBinaryPath string, pcrValuesFilePath string, eosVersion string, maxVMs int) (manager.Service, error) {
|
||||
svc, err := manager.New(qemuCfg, attestationPolicyPath, igvmMeasurementBinaryPath, pcrValuesFilePath, logger, qemu.NewVM, eosVersion, maxVMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+19
-9
@@ -7,13 +7,23 @@ COCOS_JAEGER_TRACE_RATIO=1.0
|
||||
# Manager Service Configuration
|
||||
MANAGER_INSTANCE_ID=
|
||||
MANAGER_ATTESTATION_POLICY_BINARY=../../build
|
||||
MANAGER_GRPC_CLIENT_CERT=
|
||||
MANAGER_GRPC_CLIENT_KEY=
|
||||
MANAGER_IGVMMEASURE_BINARY=../../build
|
||||
MANAGER_PCR_VALUES=/etc/cocos/pcr_values.json
|
||||
MANAGER_GRPC_SERVER_CERT=
|
||||
MANAGER_GRPC_SERVER_KEY=
|
||||
MANAGER_GRPC_SERVER_CA_CERTS=
|
||||
MANAGER_GRPC_CLIENT_CA_CERTS=
|
||||
MANAGER_GRPC_PORT=6101
|
||||
MANAGER_GRPC_HOST=0.0.0.0
|
||||
MANAGER_HTTP_SERVER_CERT=
|
||||
MANAGER_HTTP_SERVER_KEY=
|
||||
MANAGER_HTTP_SERVER_CA_CERTS=
|
||||
MANAGER_HTTP_CLIENT_CA_CERTS=
|
||||
MANAGER_HTTP_PORT=6102
|
||||
MANAGER_HTTP_HOST=0.0.0.0
|
||||
MANAGER_GRPC_TIMEOUT=60s
|
||||
MANAGER_EOS_VERSION=""
|
||||
MANAGER_MAX_VMS=10
|
||||
|
||||
# QEMU Configuration
|
||||
MANAGER_QEMU_MEMORY_SIZE=25G
|
||||
@@ -38,17 +48,17 @@ MANAGER_QEMU_VIRTIO_NET_PCI_ADDR=0x2
|
||||
MANAGER_QEMU_VIRTIO_NET_PCI_ROMFILE=
|
||||
MANAGER_QEMU_DISK_IMG_KERNEL_FILE=/etc/cocos/bzImage
|
||||
MANAGER_QEMU_DISK_IMG_ROOTFS_FILE=/etc/cocos/rootfs.cpio.gz
|
||||
MANAGER_QEMU_SEV_ID=sev0
|
||||
MANAGER_QEMU_SEV_CBITPOS=51
|
||||
MANAGER_QEMU_SEV_REDUCED_PHYS_BITS=1
|
||||
MANAGER_QEMU_SEV_SNP_ID=sev0
|
||||
MANAGER_QEMU_SEV_SNP_CBITPOS=51
|
||||
MANAGER_QEMU_SEV_SNP_REDUCED_PHYS_BITS=1
|
||||
MANAGER_QEMU_HOST_DATA=
|
||||
MANAGER_QEMU_VSOCK_ID=vhost-vsock-pci0
|
||||
MANAGER_QEMU_VSOCK_GUEST_CID=3
|
||||
MANAGER_QEMU_VSOCK_VNC=0
|
||||
MANAGER_QEMU_TDX_ID=tdx0
|
||||
MANAGER_QEMU_QUOTE_GENERATION_PORT=4050
|
||||
MANAGER_QEMU_OVMF_FILE=/usr/share/ovmf/OVMF.fd
|
||||
MANAGER_QEMU_BIN_PATH=qemu-system-x86_64
|
||||
MANAGER_QEMU_USE_SUDO=true
|
||||
MANAGER_QEMU_ENABLE_SEV=false
|
||||
MANAGER_QEMU_ENABLE_SEV_SNP=false
|
||||
MANAGER_QEMU_ENABLE_TDX=false
|
||||
MANAGER_QEMU_IGVM_FILE=/etc/cocos/coconut-qemu.igvm
|
||||
MANAGER_QEMU_ENABLE_KVM=true
|
||||
MANAGER_QEMU_MACHINE=q35
|
||||
|
||||
@@ -1,94 +1,134 @@
|
||||
module github.com/ultravioletrs/cocos
|
||||
|
||||
go 1.23.2
|
||||
go 1.24.5
|
||||
|
||||
require (
|
||||
github.com/absmach/magistrala v0.15.1
|
||||
github.com/caarlos0/env/v11 v11.3.1
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/go-kit/kit v0.13.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/google/go-sev-guest v0.12.1
|
||||
github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1
|
||||
github.com/google/go-sev-guest v0.13.0
|
||||
github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/spf13/pflag v1.0.7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/virtee/sev-snp-measure-go v0.0.0-20240530153610-e6e8dc9b6877
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
|
||||
go.opentelemetry.io/otel/trace v1.34.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/sync v0.11.0
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
golang.org/x/crypto v0.40.0
|
||||
golang.org/x/sync v0.16.0
|
||||
google.golang.org/grpc v1.74.2
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.55.0
|
||||
github.com/absmach/supermq v0.17.0
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/gce-tcb-verifier v0.3.1
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go v0.121.1 // indirect
|
||||
cloud.google.com/go/auth v0.16.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.3.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.3.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.2 // indirect
|
||||
github.com/google/gce-tcb-verifier v0.2.3-0.20240905212129-12f728a62786 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.8 // indirect
|
||||
github.com/google/go-attestation v0.5.1 // indirect
|
||||
github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba // indirect
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/api v0.235.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
moul.io/http2curl v1.0.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/absmach/certs v0.0.0-20250707105817-4c41124bba54
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/docker v28.0.1+incompatible
|
||||
github.com/docker/docker v28.3.2+incompatible
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20250408071817-8c4457b235ff
|
||||
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.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc // indirect
|
||||
github.com/google/go-tpm v0.9.3
|
||||
github.com/google/go-tpm v0.9.5
|
||||
github.com/google/go-tpm-tools v0.4.4
|
||||
github.com/google/logger v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.59.1 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
||||
+5
-10
@@ -35,19 +35,14 @@ DISK_IMG_ROOTFS_FILE=
|
||||
|
||||
KERNEL_COMMAND_LINE="quiet console=null"
|
||||
|
||||
#### Sev Config
|
||||
SEV_ID=sev0
|
||||
SEV_CBIT_POS=51
|
||||
SEV_REDUCED_PHYS_BITS=1
|
||||
SEV_HOST_DATA=
|
||||
|
||||
#### VSock Config
|
||||
VSOCK_ID=vhost-vsock-pci0
|
||||
VSOCK_GUEST_CID=3
|
||||
#### Sev-Snp Config
|
||||
SEV_SNP_ID=sev0
|
||||
SEV_SNP_CBIT_POS=51
|
||||
SEV_SNP_REDUCED_PHYS_BITS=1
|
||||
SEV_SNP_HOST_DATA=
|
||||
|
||||
BIN_PATH=qemu-system-x86_64
|
||||
USE_SUDO=false
|
||||
ENABLE_SEV=false
|
||||
ENABLE_SEV_SNP=false
|
||||
|
||||
ENABLE_KVM=true
|
||||
|
||||
+174
-8
@@ -11,14 +11,16 @@ users:
|
||||
- docker # Add cocos user to the docker group
|
||||
sudo:
|
||||
- ALL=(ALL:ALL) ALL
|
||||
shell: /sbin/nologin
|
||||
- name: algo_user
|
||||
gecos: Algorithm User
|
||||
groups:
|
||||
- docker # Add to docker group for container execution
|
||||
shell: /bin/bash
|
||||
|
||||
chpasswd:
|
||||
list: |
|
||||
cocos_user:password
|
||||
expire: False
|
||||
|
||||
ssh_pwauth: True
|
||||
sudo:
|
||||
- /usr/bin/python3 # Allow running Python
|
||||
- /home/algo_user/.wasmtime/bin/wasmtime # Allow running Wasmtime
|
||||
- /usr/bin/docker # Allow running Docker commands
|
||||
|
||||
packages:
|
||||
- curl
|
||||
@@ -27,6 +29,8 @@ packages:
|
||||
- python3
|
||||
- python3-dev
|
||||
- net-tools # Add net-tools to install the 'route' command
|
||||
- iptables-persistent # Add iptables-persistent to save firewall rules
|
||||
- sudo # Ensure sudo is installed
|
||||
|
||||
write_files:
|
||||
- path: /etc/cocos/certs/cert.pem
|
||||
@@ -134,7 +138,134 @@ write_files:
|
||||
exec /bin/cocos-agent
|
||||
permissions: "0755"
|
||||
|
||||
# Firewall configuration script
|
||||
- path: /cocos_init/setup_firewall.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
|
||||
# Flush existing rules
|
||||
iptables -F
|
||||
iptables -X
|
||||
iptables -t nat -F
|
||||
iptables -t nat -X
|
||||
iptables -t mangle -F
|
||||
iptables -t mangle -X
|
||||
|
||||
# Set default policies to DROP
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
|
||||
# Allow loopback interface
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
iptables -A OUTPUT -o lo -j ACCEPT
|
||||
|
||||
# Allow established and related connections
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Allow port 7002 (TCP) for incoming connections
|
||||
iptables -A INPUT -p tcp --dport 7002 -j ACCEPT
|
||||
|
||||
# Save rules to persist after reboot
|
||||
mkdir -p /etc/iptables
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
|
||||
echo "Firewall configured to allow only port 7002"
|
||||
permissions: "0755"
|
||||
|
||||
# Setup script for algo_user
|
||||
- path: /cocos_init/setup_algo_user.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
|
||||
# Create datasets directory with the right permissions
|
||||
mkdir -p /datasets
|
||||
chown root:root /datasets
|
||||
chmod 755 /datasets
|
||||
|
||||
# Create a home directory for algo_user
|
||||
mkdir -p /home/algo_user
|
||||
chown algo_user:algo_user /home/algo_user
|
||||
chmod 750 /home/algo_user
|
||||
|
||||
# Set up Wasmtime for algo_user
|
||||
mkdir -p /home/algo_user/.wasmtime/bin
|
||||
|
||||
# Link system Wasmtime to user's directory
|
||||
ln -s /root/.wasmtime/bin/wasmtime /home/algo_user/.wasmtime/bin/wasmtime
|
||||
chown -R algo_user:algo_user /home/algo_user/.wasmtime
|
||||
|
||||
# Configure sudoers for algo_user
|
||||
cat > /etc/sudoers.d/algo_user << EOF
|
||||
algo_user ALL=(ALL) NOPASSWD: /usr/bin/python3
|
||||
algo_user ALL=(ALL) NOPASSWD: /home/algo_user/.wasmtime/bin/wasmtime
|
||||
algo_user ALL=(ALL) NOPASSWD: /usr/bin/docker
|
||||
Defaults:algo_user !requiretty
|
||||
EOF
|
||||
|
||||
chmod 440 /etc/sudoers.d/algo_user
|
||||
|
||||
# Create a .bashrc file that restricts navigation to /datasets
|
||||
cat > /home/algo_user/.bashrc << EOF
|
||||
# .bashrc for algo_user
|
||||
|
||||
# Restrict navigation outside /datasets
|
||||
function cd() {
|
||||
if [[ "\$1" == /* && "\$1" != "/datasets"* && "\$1" != "/home/algo_user"* ]]; then
|
||||
echo "Access denied: You can only access files in /datasets or your home directory"
|
||||
return 1
|
||||
else
|
||||
builtin cd "\$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Set PATH to include necessary binaries
|
||||
export PATH=/usr/bin:/bin:/usr/local/bin:/home/algo_user/.wasmtime/bin
|
||||
|
||||
# Set default directory
|
||||
if [[ \$PWD != "/datasets"* && \$PWD != "/home/algo_user"* ]]; then
|
||||
cd /datasets
|
||||
fi
|
||||
EOF
|
||||
|
||||
chown algo_user:algo_user /home/algo_user/.bashrc
|
||||
chmod 644 /home/algo_user/.bashrc
|
||||
|
||||
echo "algo_user setup completed successfully"
|
||||
permissions: "0755"
|
||||
|
||||
# IMA setup for first boot
|
||||
- path: /cocos_init/linux_ima_init.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
|
||||
GRUB_FILE="/etc/default/grub"
|
||||
|
||||
if ! grep -q '^GRUB_CMDLINE_LINUX=.*ima_policy' "$GRUB_FILE"; then
|
||||
echo "First boot, rebooting with fix IMA appraise policy"
|
||||
|
||||
NEW_PARAMS='ima_policy=tcb'
|
||||
|
||||
# Modify the GRUB_CMDLINE_LINUX line
|
||||
sed -i -E \
|
||||
"s#^(GRUB_CMDLINE_LINUX=\")(.*)(\")#\1\2 $NEW_PARAMS\3#" \
|
||||
"$GRUB_FILE"
|
||||
|
||||
echo "Updated GRUB_CMDLINE_LINUX:"
|
||||
grep "^GRUB_CMDLINE_LINUX=" "$GRUB_FILE"
|
||||
|
||||
sudo update-grub
|
||||
sudo reboot
|
||||
else
|
||||
sudo find / -fstype ext4 -type f -uid 0 -exec dd if='{}' of=/dev/null count=0 status=none \;
|
||||
fi
|
||||
permissions: "0755"
|
||||
|
||||
runcmd:
|
||||
# Enable Linux IMA with fix policy
|
||||
- echo "Enable Linux IMA with fix policy and rebooting if necessary"
|
||||
- sh /cocos_init/linux_ima_init.sh
|
||||
|
||||
# Create necessary directories
|
||||
- mkdir -p /cocos
|
||||
- mkdir -p /cocos_init
|
||||
@@ -163,6 +294,41 @@ runcmd:
|
||||
- sh ./get-docker.sh && echo "Docker installed successfully" || echo "Failed to install Docker"
|
||||
- usermod -aG docker cocos_user && echo "Added cocos_user to the docker group" || echo "Failed to add cocos_user to the docker group"
|
||||
|
||||
# Disable SSH service
|
||||
- echo "Disabling SSH service..."
|
||||
- systemctl disable ssh.service && echo "SSH service disabled successfully" || echo "Failed to disable SSH service"
|
||||
- systemctl disable sshd.service && echo "SSHD service disabled successfully" || echo "Failed to disable SSHD service"
|
||||
- systemctl stop ssh.service && echo "SSH service stopped successfully" || echo "Failed to stop SSH service"
|
||||
- systemctl stop sshd.service && echo "SSHD service stopped successfully" || echo "Failed to stop SSHD service"
|
||||
|
||||
# Completely remove SSH server
|
||||
- echo "Removing SSH server..."
|
||||
- apt-get purge -y openssh-server && echo "SSH server removed successfully" || echo "Failed to remove SSH server"
|
||||
- apt-get autoremove -y && echo "Removed dependencies successfully" || echo "Failed to remove dependencies"
|
||||
|
||||
# Set up firewall to only allow port 7002
|
||||
- echo "Setting up firewall..."
|
||||
- bash /cocos_init/setup_firewall.sh && echo "Firewall configured successfully" || echo "Failed to configure firewall"
|
||||
|
||||
# Ensure iptables rules persist after reboot
|
||||
- echo "Ensuring iptables rules persist after reboot..."
|
||||
- systemctl enable netfilter-persistent && echo "netfilter-persistent service enabled successfully" || echo "Failed to enable netfilter-persistent service"
|
||||
- systemctl start netfilter-persistent && echo "netfilter-persistent service started successfully" || echo "Failed to start netfilter-persistent service"
|
||||
|
||||
# Set up algo_user environment
|
||||
- echo "Setting up algo_user environment..."
|
||||
- bash /cocos_init/setup_algo_user.sh && echo "algo_user environment setup successfully" || echo "Failed to set up algo_user environment"
|
||||
|
||||
# Create datasets directory and set permissions
|
||||
- echo "Creating datasets directory..."
|
||||
- mkdir -p /datasets && echo "datasets directory created successfully" || echo "Failed to create datasets directory"
|
||||
- chown root:root /datasets && echo "datasets ownership set successfully" || echo "Failed to set datasets ownership"
|
||||
- chmod 755 /datasets && echo "datasets permissions set successfully" || echo "Failed to set datasets permissions"
|
||||
|
||||
# Add algo_user to the docker group
|
||||
- echo "Adding algo_user to the docker group..."
|
||||
- usermod -aG docker algo_user && echo "Added algo_user to the docker group" || echo "Failed to add algo_user to the docker group"
|
||||
|
||||
# Reload systemd and enable the service
|
||||
- echo "[ COCOS AGENT SETUP ] Reloading systemd daemon..."
|
||||
- systemctl daemon-reload && echo "[ COCOS AGENT SETUP ] Systemd daemon reloaded successfully" || echo "[ COCOS AGENT SETUP ] Failed to reload systemd daemon"
|
||||
@@ -171,4 +337,4 @@ runcmd:
|
||||
- echo "[ COCOS AGENT SETUP ] Starting cocos-agent.service..."
|
||||
- systemctl start cocos-agent.service && echo "[ COCOS AGENT SETUP ] cocos-agent.service started successfully" || echo "[ COCOS AGENT SETUP ] Failed to start cocos-agent.service"
|
||||
|
||||
final_message: "Cocos agent setup complete. Verify logs to confirm successful service startup."
|
||||
final_message: "Cocos agent and algo_user setup complete. Verify logs to confirm successful service startup."
|
||||
|
||||
+9
-14
@@ -72,23 +72,19 @@ construct_qemu_args() {
|
||||
# Network configuration
|
||||
args+=("-netdev" "user,id=$NET_DEV_ID,hostfwd=tcp::$NET_DEV_HOST_FWD_AGENT-:$NET_DEV_GUEST_FWD_AGENT")
|
||||
args+=("-device" "virtio-net-pci,disable-legacy=$VIRTIO_NET_PCI_DISABLE_LEGACY,iommu_platform=$VIRTIO_NET_PCI_IOMMU_PLATFORM,netdev=$NET_DEV_ID,addr=$VIRTIO_NET_PCI_ADDR,romfile=$VIRTIO_NET_PCI_ROMFILE")
|
||||
args+=("-device" "vhost-vsock-pci,id=$VSOCK_ID,guest-cid=$VSOCK_GUEST_CID")
|
||||
|
||||
# SEV (if enabled)
|
||||
if [ "$ENABLE_SEV" == "true" ] || [ "$ENABLE_SEV_SNP" == "true" ]; then
|
||||
sev_type="sev-guest"
|
||||
|
||||
# SEV_SNP (if enabled)
|
||||
if [ "$ENABLE_SEV_SNP" == "true" ]; then
|
||||
kernel_hash=""
|
||||
host_data=""
|
||||
|
||||
args+=("-machine" "confidential-guest-support=$SEV_ID,memory-backend=$MEM_ID")
|
||||
args+=("-machine" "confidential-guest-support=$SEV_SNP_ID,memory-backend=$MEM_ID")
|
||||
|
||||
if [ "$ENABLE_SEV_SNP" == "true" ]; then
|
||||
args+=("-bios" "$OVMF_CODE_FILE")
|
||||
sev_type="sev-snp-guest"
|
||||
args+=("-bios" "$OVMF_CODE_FILE")
|
||||
sev_snp_type="sev-snp-guest"
|
||||
|
||||
if [ -n "$SEV_HOST_DATA" ]; then
|
||||
host_data=",host-data=$SEV_HOST_DATA"
|
||||
fi
|
||||
if [ -n "$SEV_SNP_HOST_DATA" ]; then
|
||||
host_data=",host-data=$SEV_SNP_HOST_DATA"
|
||||
fi
|
||||
|
||||
if [ "$ENABLE_KERNEL_HASH" == "true" ]; then
|
||||
@@ -96,7 +92,7 @@ construct_qemu_args() {
|
||||
fi
|
||||
|
||||
args+=("-object" "memory-backend-memfd,id=$MEM_ID,size=$MEMORY_SIZE,share=true,prealloc=false")
|
||||
args+=("-object" "$sev_type,id=$SEV_ID,cbitpos=$SEV_CBIT_POS,reduced-phys-bits=$SEV_REDUCED_PHYS_BITS$kernel_hash$host_data")
|
||||
args+=("-object" "$sev_snp_type,id=$SEV_SNP_ID,cbitpos=$SEV_SNP_CBIT_POS,reduced-phys-bits=$SEV_SNP_REDUCED_PHYS_BITS$kernel_hash$host_data")
|
||||
fi
|
||||
|
||||
# Disk image configuration
|
||||
@@ -111,7 +107,6 @@ construct_qemu_args() {
|
||||
fi
|
||||
|
||||
args+=("-monitor" "$MONITOR")
|
||||
args+=("-no-reboot")
|
||||
args+=("-vnc" ":9")
|
||||
|
||||
echo "${args[@]}"
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ HAL uses [Buildroot](https://buildroot.org/)'s [_External Tree_ mechanism](https
|
||||
git clone git@github.com:ultravioletrs/cocos.git
|
||||
git clone git@github.com:buildroot/buildroot.git
|
||||
cd buildroot
|
||||
git checkout 2024.11-rc2
|
||||
git checkout 2025.05-rc1
|
||||
make BR2_EXTERNAL=../cocos/hal/linux cocos_defconfig
|
||||
# Execute 'make menuconfig' only if you want to make additional configuration changes to Buildroot.
|
||||
make menuconfig
|
||||
|
||||
@@ -76,3 +76,36 @@ CONFIG_9P_FS_SECURITY=y
|
||||
CONFIG_TCG_TPM=y
|
||||
CONFIG_TCG_TPM2_HMAC=y
|
||||
CONFIG_TCG_PLATFORM=y
|
||||
|
||||
# Linux IMA
|
||||
CONFIG_SECURITY=y
|
||||
CONFIG_SECURITYFS=y
|
||||
CONFIG_INTEGRITY=y
|
||||
CONFIG_INTEGRITY_SIGNATURE=y
|
||||
CONFIG_IMA=y
|
||||
CONFIG_IMA_MEASURE_PCR_IDX=10 # optional but good
|
||||
CONFIG_IMA_LSM_RULES=y
|
||||
CONFIG_IMA_APPRAISE=y # if you want appraisal (signature checking)
|
||||
CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng"
|
||||
CONFIG_KEYS=y
|
||||
CONFIG_ENCRYPTED_KEYS=y # if you plan to use encrypted keys
|
||||
CONFIG_IMA_DEFAULT_HASH="sha256" # or sha1 if you prefer
|
||||
|
||||
#Intel TDX
|
||||
CONFIG_EXPERT=y
|
||||
CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=y
|
||||
CONFIG_KVM_SW_PROTECTED_VM=y
|
||||
CONFIG_KVM_INTEL=y
|
||||
CONFIG_PCI_MSI=y
|
||||
CONFIG_IRQ_REMAP=y
|
||||
CONFIG_X86_X2APIC=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_X86_SGX=y
|
||||
CONFIG_X86_SGX_KVM=y
|
||||
CONFIG_INTEL_TDX_GUEST=y
|
||||
CONFIG_KSM=n
|
||||
CONFIG_EISA=n
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
CONFIG_VIRTIO_NET=y
|
||||
CONFIG_TDX_GUEST_DRIVER=y
|
||||
|
||||
@@ -7,11 +7,11 @@ Before=docker.service
|
||||
WorkingDirectory=/cocos
|
||||
StandardOutput=file:/var/log/cocos/agent.stdout
|
||||
StandardError=file:/var/log/cocos/agent.stderr
|
||||
|
||||
EnvironmentFile=/etc/cocos/environment
|
||||
|
||||
ExecStartPre=/cocos_init/agent_setup.sh
|
||||
ExecStart=/cocos_init/agent_start_script.sh
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/ultravioletrs/cocos/agent/cvms"
|
||||
)
|
||||
|
||||
+98
-111
@@ -5,27 +5,20 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-sev-guest/client"
|
||||
agentgrpc "github.com/ultravioletrs/cocos/agent/api/grpc"
|
||||
"github.com/ultravioletrs/cocos/agent/auth"
|
||||
"github.com/ultravioletrs/cocos/internal/server"
|
||||
"github.com/ultravioletrs/cocos/pkg/atls"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -50,18 +43,22 @@ const (
|
||||
|
||||
type Server struct {
|
||||
server.BaseServer
|
||||
mu sync.RWMutex
|
||||
server *grpc.Server
|
||||
registerService serviceRegister
|
||||
quoteProvider client.LeveledQuoteProvider
|
||||
authSvc auth.Authenticator
|
||||
health *health.Server
|
||||
registerService serviceRegister
|
||||
authSvc auth.Authenticator
|
||||
caUrl string
|
||||
cvmId string
|
||||
started bool
|
||||
stopped bool
|
||||
}
|
||||
|
||||
type serviceRegister func(srv *grpc.Server)
|
||||
|
||||
var _ server.Server = (*Server)(nil)
|
||||
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.ServerConfiguration, registerService serviceRegister, logger *slog.Logger, qp client.LeveledQuoteProvider, authSvc auth.Authenticator) server.Server {
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.ServerConfiguration, registerService serviceRegister, logger *slog.Logger, authSvc auth.Authenticator, caUrl string, cvmId string) server.Server {
|
||||
base := config.GetBaseConfig()
|
||||
listenFullAddress := fmt.Sprintf("%s:%s", base.Host, base.Port)
|
||||
return &Server{
|
||||
@@ -74,12 +71,25 @@ func New(ctx context.Context, cancel context.CancelFunc, name string, config ser
|
||||
Logger: logger,
|
||||
},
|
||||
registerService: registerService,
|
||||
quoteProvider: qp,
|
||||
authSvc: authSvc,
|
||||
caUrl: caUrl,
|
||||
cvmId: cvmId,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
s.mu.Lock()
|
||||
if s.started {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("server already started")
|
||||
}
|
||||
if s.stopped {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("server already stopped")
|
||||
}
|
||||
s.started = true
|
||||
s.mu.Unlock()
|
||||
|
||||
errCh := make(chan error)
|
||||
grpcServerOptions := []grpc.ServerOption{
|
||||
grpc.StatsHandler(otelgrpc.NewServerHandler()),
|
||||
@@ -92,37 +102,57 @@ func (s *Server) Start() error {
|
||||
}
|
||||
|
||||
creds := grpc.Creds(insecure.NewCredentials())
|
||||
var listener net.Listener
|
||||
|
||||
c := s.Config.GetBaseConfig()
|
||||
if agCfg, ok := s.Config.(server.AgentConfig); ok && agCfg.AttestedTLS {
|
||||
certificateBytes, privateKeyBytes, err := generateCertificatesForATLS()
|
||||
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},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
GetCertificate: atls.GetCertificate(s.caUrl, s.cvmId),
|
||||
}
|
||||
|
||||
var mtls bool
|
||||
mtls = false
|
||||
|
||||
// Loading Server CA file
|
||||
rootCA, err := loadCertFile(c.ServerCAFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load server 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 server ca to tls.Config")
|
||||
}
|
||||
mtls = true
|
||||
}
|
||||
|
||||
// Loading Client CA File
|
||||
clientCA, err := loadCertFile(c.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")
|
||||
}
|
||||
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
mtls = true
|
||||
}
|
||||
|
||||
creds = grpc.Creds(credentials.NewTLS(tlsConfig))
|
||||
|
||||
listener, err = atls.Listen(
|
||||
s.Address,
|
||||
certificateBytes,
|
||||
privateKeyBytes,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Listener for aTLS: %w", err)
|
||||
if mtls {
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with Attested mTLS", s.Name, s.Address))
|
||||
} else {
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with Attested TLS", s.Name, s.Address))
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with Attested TLS", s.Name, s.Address))
|
||||
} else {
|
||||
c := s.Config.GetBaseConfig()
|
||||
switch {
|
||||
case c.CertFile != "" || c.KeyFile != "":
|
||||
certificate, err := loadX509KeyPair(c.CertFile, c.KeyFile)
|
||||
@@ -173,32 +203,34 @@ func (s *Server) Start() error {
|
||||
default:
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, c.CertFile, c.KeyFile))
|
||||
}
|
||||
|
||||
listener, err = net.Listen("tcp", s.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
|
||||
}
|
||||
default:
|
||||
var err error
|
||||
|
||||
listener, err = net.Listen("tcp", s.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", s.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
|
||||
}
|
||||
|
||||
grpcServerOptions = append(grpcServerOptions, creds)
|
||||
|
||||
s.mu.Lock()
|
||||
s.server = grpc.NewServer(grpcServerOptions...)
|
||||
s.health = health.NewServer()
|
||||
grpchealth.RegisterHealthServer(s.server, s.health)
|
||||
s.registerService(s.server)
|
||||
s.health.SetServingStatus(s.Name, grpchealth.HealthCheckResponse_SERVING)
|
||||
s.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
errCh <- s.server.Serve(listener)
|
||||
s.mu.RLock()
|
||||
server := s.server
|
||||
s.mu.RUnlock()
|
||||
|
||||
if server != nil {
|
||||
errCh <- server.Serve(listener)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
@@ -211,19 +243,33 @@ func (s *Server) Start() error {
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.stopped {
|
||||
return nil
|
||||
}
|
||||
s.stopped = true
|
||||
|
||||
defer s.Cancel()
|
||||
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
defer close(c)
|
||||
s.health.Shutdown()
|
||||
s.server.GracefulStop()
|
||||
if s.health != nil {
|
||||
s.health.Shutdown()
|
||||
}
|
||||
if s.server != nil {
|
||||
s.server.GracefulStop()
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
case <-time.After(stopWaitTime):
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s gRPC service shutdown at %s", s.Name, s.Address))
|
||||
|
||||
s.Logger.Info(fmt.Sprintf("%s gRPC service shutdown at %s", s.Name, s.Address))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -239,6 +285,8 @@ func readFileOrData(input string) ([]byte, error) {
|
||||
data, err := os.ReadFile(input)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return []byte(input), nil
|
||||
@@ -257,64 +305,3 @@ func loadX509KeyPair(certfile, keyfile string) (tls.Certificate, error) {
|
||||
|
||||
return tls.X509KeyPair(cert, key)
|
||||
}
|
||||
|
||||
func generateCertificatesForATLS() ([]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)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
cert, err := x509.ParseCertificate(certDERBytes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
|
||||
pubKeyDER, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to marshal public key to DER format: %w", err)
|
||||
}
|
||||
|
||||
if err := vtpm.ExtendPCR(vtpm.PCR15, pubKeyDER); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to extend vTPM PCR with public key: %w", err)
|
||||
}
|
||||
|
||||
return certBytes, keyBytes, nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
authmocks "github.com/ultravioletrs/cocos/agent/auth/mocks"
|
||||
"github.com/ultravioletrs/cocos/internal/server"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
@@ -31,28 +30,6 @@ const bufSize = 1024 * 1024
|
||||
|
||||
var lis *bufconn.Listener
|
||||
|
||||
type DummyRWC struct{}
|
||||
|
||||
// Read fills p with byte(len(p)) and returns len(p).
|
||||
func (l *DummyRWC) Read(p []byte) (int, error) {
|
||||
n := len(p)
|
||||
// Fill each byte in p with the value of n as a byte.
|
||||
for i := range p {
|
||||
p[i] = byte(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Write simply returns len(p) indicating that all bytes were written.
|
||||
func (l *DummyRWC) Write(p []byte) (int, error) {
|
||||
// In this simple implementation, we ignore the data.
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (l *DummyRWC) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
lis = bufconn.Listen(bufSize)
|
||||
}
|
||||
@@ -70,10 +47,9 @@ func TestNew(t *testing.T) {
|
||||
},
|
||||
}
|
||||
logger := slog.Default()
|
||||
qp := new(mocks.LeveledQuoteProvider)
|
||||
authSvc := new(authmocks.Authenticator)
|
||||
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, qp, authSvc)
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, authSvc, "", "")
|
||||
|
||||
assert.NotNil(t, srv)
|
||||
assert.IsType(t, &Server{}, srv)
|
||||
@@ -120,10 +96,9 @@ func TestServerStartWithTLSFile(t *testing.T) {
|
||||
|
||||
logBuffer := &ThreadSafeBuffer{}
|
||||
logger := slog.New(slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
qp := new(mocks.LeveledQuoteProvider)
|
||||
authSvc := new(authmocks.Authenticator)
|
||||
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, qp, authSvc)
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, authSvc, "", "")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
@@ -167,10 +142,9 @@ func TestServerStartWithmTLSFile(t *testing.T) {
|
||||
|
||||
logBuffer := &ThreadSafeBuffer{}
|
||||
logger := slog.New(slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
qp := new(mocks.LeveledQuoteProvider)
|
||||
authSvc := new(authmocks.Authenticator)
|
||||
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, qp, authSvc)
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, authSvc, "", "")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
@@ -207,10 +181,9 @@ func TestServerStop(t *testing.T) {
|
||||
}
|
||||
buf := &ThreadSafeBuffer{}
|
||||
logger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
qp := new(mocks.LeveledQuoteProvider)
|
||||
authSvc := new(authmocks.Authenticator)
|
||||
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, qp, authSvc)
|
||||
srv := New(ctx, cancel, "TestServer", config, func(srv *grpc.Server) {}, logger, authSvc, "", "")
|
||||
|
||||
go func() {
|
||||
err := srv.Start()
|
||||
@@ -282,7 +255,7 @@ func (b *ThreadSafeBuffer) String() string {
|
||||
}
|
||||
|
||||
func TestServerInitializationAndStartup(t *testing.T) {
|
||||
vtpm.ExternalTPM = &DummyRWC{}
|
||||
vtpm.ExternalTPM = &vtpm.DummyRWC{}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -332,60 +305,53 @@ func TestServerInitializationAndStartup(t *testing.T) {
|
||||
expectedLog: "failed to load auth certificates",
|
||||
},
|
||||
{
|
||||
name: "mTLS Server Startup",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
setupCallback: setupMTLSConfig,
|
||||
expectedLog: "TestServer service gRPC server listening at localhost:0 with TLS",
|
||||
},
|
||||
{
|
||||
name: "mTLS Server Startup with Invalid Root CA",
|
||||
name: "maTLS Server Startup",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
ServerCAFile: "invalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
setupCallback: setupInvalidRootCAConfig,
|
||||
expectError: true,
|
||||
expectedLog: "failed to append root ca to tls.Config",
|
||||
},
|
||||
{
|
||||
name: "mTLS Server Startup with Invalid Client CA",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
ServerCAFile: "invalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
setupCallback: setupInvalidClientCAConfig,
|
||||
expectError: true,
|
||||
expectedLog: "failed to append client ca to tls.Config",
|
||||
},
|
||||
{
|
||||
name: "Attested TLS Server Startup",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
ServerCAFile: "",
|
||||
ClientCAFile: "",
|
||||
},
|
||||
},
|
||||
AttestedTLS: true,
|
||||
},
|
||||
expectedLog: "TestServer service gRPC server listening at localhost:0 with Attested TLS",
|
||||
setupCallback: setupMTLSConfig,
|
||||
expectError: false,
|
||||
expectedLog: "with Attested mTLS",
|
||||
},
|
||||
{
|
||||
name: "maTLS Server Startup with Invalid Server CA file",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
ServerCAFile: "invalid",
|
||||
},
|
||||
},
|
||||
AttestedTLS: true,
|
||||
},
|
||||
setupCallback: setupInvalidRootCAConfig,
|
||||
expectError: true,
|
||||
expectedLog: "failed to load server ca file",
|
||||
},
|
||||
{
|
||||
name: "maTLS Server Startup with Invalid Clinet CA file",
|
||||
config: server.AgentConfig{
|
||||
ServerConfig: server.ServerConfig{
|
||||
BaseConfig: server.BaseConfig{
|
||||
Host: "localhost",
|
||||
Port: "0",
|
||||
ServerCAFile: "invalid",
|
||||
},
|
||||
},
|
||||
AttestedTLS: true,
|
||||
},
|
||||
setupCallback: setupInvalidClientCAConfig,
|
||||
expectError: true,
|
||||
expectedLog: "failed to load client ca file",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -399,10 +365,9 @@ func TestServerInitializationAndStartup(t *testing.T) {
|
||||
|
||||
logBuffer := &ThreadSafeBuffer{}
|
||||
logger := slog.New(slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
qp := new(mocks.LeveledQuoteProvider)
|
||||
authSvc := new(authmocks.Authenticator)
|
||||
|
||||
srv := New(ctx, cancel, "TestServer", tc.config, func(srv *grpc.Server) {}, logger, qp, authSvc)
|
||||
srv := New(ctx, cancel, "TestServer", tc.config, func(srv *grpc.Server) {}, logger, authSvc, "", "")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -20,7 +20,7 @@ func (_m *Server) EXPECT() *Server_Expecter {
|
||||
return &Server_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Start provides a mock function with given fields:
|
||||
// Start provides a mock function with no fields
|
||||
func (_m *Server) Start() error {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -65,7 +65,7 @@ func (_c *Server_Start_Call) RunAndReturn(run func() error) *Server_Start_Call {
|
||||
return _c
|
||||
}
|
||||
|
||||
// Stop provides a mock function with given fields:
|
||||
// Stop provides a mock function with no fields
|
||||
func (_m *Server) Stop() error {
|
||||
ret := _m.Called()
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ type ServerConfiguration interface {
|
||||
type BaseConfig struct {
|
||||
Host string `env:"HOST" envDefault:"localhost"`
|
||||
Port string `env:"PORT" envDefault:"7001"`
|
||||
ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""`
|
||||
ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""`
|
||||
CertFile string `env:"SERVER_CERT" envDefault:""`
|
||||
KeyFile string `env:"SERVER_KEY" envDefault:""`
|
||||
ClientCAFile string `env:"CLIENT_CA_CERTS" envDefault:""`
|
||||
|
||||
+124
-123
@@ -6,64 +6,74 @@ Manager service provides a barebones gRPC API and Service interface implementati
|
||||
|
||||
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 |
|
||||
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------- |
|
||||
| COCOS_JAEGER_URL | The URL for the Jaeger tracing endpoint. | http://localhost:4318 |
|
||||
| COCOS_JAEGER_TRACE_RATIO | The ratio of traces to sample. | 1.0 |
|
||||
| MANAGER_INSTANCE_ID | The instance ID for the manager service. | |
|
||||
| MANAGER_ATTESTATION_POLICY_BINARY | The file path for the attestation policy and igvmmeassure binaries. | ../../build |
|
||||
| MANAGER_GRPC_CLIENT_CERT | The file path for the client certificate. | |
|
||||
| MANAGER_GRPC_CLIENT_KEY | The file path for the client private key. | |
|
||||
| MANAGER_GRPC_SERVER_CA_CERTS | The file path for the server CA certificate(s). | |
|
||||
| MANAGER_GRPC_URL | The URL for the gRPC endpoint. | localhost:7001 |
|
||||
| MANAGER_GRPC_TIMEOUT | The timeout for gRPC requests. | 60s |
|
||||
| MANAGER_EOS_VERSION | The EOS version used for booting SVMs. | |
|
||||
| MANAGER_INSTANCE_ID | Manager service instance ID | |
|
||||
| MANAGER_QEMU_MEMORY_SIZE | The total memory size for the virtual machine. Can be specified in a human-readable format like "2048M" or "4G". | 2048M |
|
||||
| MANAGER_QEMU_MEMORY_SLOTS | The number of memory slots for the virtual machine. | 5 |
|
||||
| MANAGER_QEMU_MAX_MEMORY | The maximum memory size for the virtual machine. Can be specified in a human-readable format like "30G". | 30G |
|
||||
| MANAGER_QEMU_OVMF_CODE_IF | The interface type for the OVMF code. | pflash |
|
||||
| MANAGER_QEMU_OVMF_CODE_FORMAT | The format of the OVMF code file. | raw |
|
||||
| MANAGER_QEMU_OVMF_CODE_UNIT | The unit number for the OVMF code. | 0 |
|
||||
| MANAGER_QEMU_OVMF_CODE_FILE | The file path for the OVMF code. | /usr/share/OVMF/OVMF_CODE.fd |
|
||||
| MANAGER_QEMU_OVMF_VERSION | The version number of EDKII from which OVMF was built | edk2-stable202408 |
|
||||
| MANAGER_QEMU_OVMF_CODE_READONLY | Whether the OVMF code should be read-only. | on |
|
||||
| MANAGER_QEMU_OVMF_VARS_IF | The interface type for the OVMF variables. | pflash |
|
||||
| MANAGER_QEMU_OVMF_VARS_FORMAT | The format of the OVMF variables file. | raw |
|
||||
| MANAGER_QEMU_OVMF_VARS_UNIT | The unit number for the OVMF variables. | 1 |
|
||||
| MANAGER_QEMU_OVMF_VARS_FILE | The file path for the OVMF variables. | /usr/share/OVMF/OVMF_VARS.fd |
|
||||
| MANAGER_QEMU_NETDEV_ID | The ID for the network device. | vmnic |
|
||||
| MANAGER_QEMU_HOST_FWD_AGENT | The port number for the host forward agent. | 7020 |
|
||||
| MANAGER_QEMU_GUEST_FWD_AGENT | The port number for the guest forward agent. | 7002 |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_DISABLE_LEGACY | Whether to disable the legacy PCI device. | on |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_IOMMU_PLATFORM | Whether to enable the IOMMU platform for the virtio-net PCI device. | true |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_ADDR | The PCI address for the virtio-net PCI device. | 0x2 |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_ROMFILE | The file path for the ROM image for the virtio-net PCI device. | |
|
||||
| MANAGER_QEMU_DISK_IMG_KERNEL_FILE | The file path for the kernel image. | img/bzImage |
|
||||
| MANAGER_QEMU_DISK_IMG_ROOTFS_FILE | The file path for the root filesystem image. | img/rootfs.cpio.gz |
|
||||
| MANAGER_QEMU_SEV_ID | The ID for the Secure Encrypted Virtualization (SEV) device. | sev0 |
|
||||
| MANAGER_QEMU_SEV_CBITPOS | The position of the C-bit in the physical address. | 51 |
|
||||
| MANAGER_QEMU_SEV_REDUCED_PHYS_BITS | The number of reduced physical address bits for SEV. | 1 |
|
||||
| MANAGER_QEMU_ENABLE_HOST_DATA | Enable additional data for the SEV host. | false |
|
||||
| MANAGER_QEMU_HOST_DATA | Additional data for the SEV host. | |
|
||||
| MANAGER_QEMU_IGVM_ID | The ID of the IGVM file. | igvm0 |
|
||||
| MANAGER_QEMU_IGVM_FILE | The file path to the IGVM file. | /root/coconut-qemu.igvm |
|
||||
| MANAGER_QEMU_VSOCK_ID | The ID for the virtual socket device. | vhost-vsock-pci0 |
|
||||
| MANAGER_QEMU_VSOCK_GUEST_CID | The guest-side CID (Context ID) for the virtual socket device. | 3 |
|
||||
| MANAGER_QEMU_VSOCK_VNC | Whether to enable the virtual socket device for VNC. | 0 |
|
||||
| MANAGER_QEMU_BIN_PATH | The file path for the QEMU binary. | qemu-system-x86_64 |
|
||||
| MANAGER_QEMU_USE_SUDO | Whether to use sudo to run QEMU. | false |
|
||||
| MANAGER_QEMU_ENABLE_SEV | Whether to enable Secure Encrypted Virtualization (SEV). | false |
|
||||
| MANAGER_QEMU_ENABLE_SEV_SNP | Whether to enable Secure Nested Paging (SEV-SNP). | true |
|
||||
| MANAGER_QEMU_ENABLE_KVM | Whether to enable the Kernel-based Virtual Machine (KVM) acceleration. | true |
|
||||
| MANAGER_QEMU_MACHINE | The machine type for QEMU. | q35 |
|
||||
| MANAGER_QEMU_CPU | The CPU model for QEMU. | EPYC |
|
||||
| MANAGER_QEMU_SMP_COUNT | The number of virtual CPUs. | 4 |
|
||||
| MANAGER_QEMU_SMP_MAXCPUS | The maximum number of virtual CPUs. | 64 |
|
||||
| MANAGER_QEMU_MEM_ID | The ID for the memory device. | ram1 |
|
||||
| MANAGER_QEMU_NO_GRAPHIC | Whether to disable the graphical display. | true |
|
||||
| MANAGER_QEMU_MONITOR | The type of monitor to use. | pty |
|
||||
| MANAGER_QEMU_HOST_FWD_RANGE | The range of host ports to forward. | 6100-6200 |
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------ |
|
||||
| COCOS_JAEGER_URL | The URL for the Jaeger tracing endpoint. | http://localhost:4318 |
|
||||
| COCOS_JAEGER_TRACE_RATIO | The ratio of traces to sample. | 1.0 |
|
||||
| MANAGER_INSTANCE_ID | The instance ID for the manager service. | |
|
||||
| MANAGER_ATTESTATION_POLICY_BINARY | The file path for the attestation policy binarie. | ../../build/attestation_policy |
|
||||
| MANAGER_IGVMMEASURE_BINARY | The file path for the igvmmeasure binarie. | ../../build/igvmmeasure |
|
||||
| MANAGER_PCR_VALUES | The file path for the file with the expected PCR values. | |
|
||||
| MANAGER_HTTP_HOST | Manager service HTTP host | "" |
|
||||
| MANAGER_HTTP_PORT | Manager service HTTP port | 7003 |
|
||||
| MANAGER_HTTP_SERVER_CERT | Manager to HTTP server certificate in pem format | "" |
|
||||
| MANAGER_HTTP_SERVER_KEY | Path to HTTP server key in pem format | "" |
|
||||
| MANAGER_HTTP_SERVER_CA_CERTS | Path to HTTP server CA certificate | "" |
|
||||
| MANAGER_HTTP_CLIENT_CA_CERTS | Path to HTTP client CA certificate | "" |
|
||||
| MANAGER_GRPC_HOST | Manager service gRPC host | "" |
|
||||
| MANAGER_GRPC_PORT | Manager service gRPC port | 7001 |
|
||||
| MANAGER_GRPC_SERVER_CERT | Path to gRPC server certificate in pem format | "" |
|
||||
| MANAGER_GRPC_SERVER_KEY | Path to gRPC 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 | "" |
|
||||
| MANAGER_EOS_VERSION | The EOS version used for booting CVMs. | |
|
||||
| MANAGER_INSTANCE_ID | Manager service instance ID | |
|
||||
| MANAGER_QEMU_MEMORY_SIZE | The total memory size for the virtual machine. Can be specified in a human-readable format like "2048M" or "4G". | 2048M |
|
||||
| MANAGER_QEMU_MEMORY_SLOTS | The number of memory slots for the virtual machine. | 5 |
|
||||
| MANAGER_QEMU_MAX_MEMORY | The maximum memory size for the virtual machine. Can be specified in a human-readable format like "30G". | 30G |
|
||||
| MANAGER_QEMU_OVMF_CODE_IF | The interface type for the OVMF code. | pflash |
|
||||
| MANAGER_QEMU_OVMF_CODE_FORMAT | The format of the OVMF code file. | raw |
|
||||
| MANAGER_QEMU_OVMF_CODE_UNIT | The unit number for the OVMF code. | 0 |
|
||||
| MANAGER_QEMU_OVMF_CODE_FILE | The file path for the OVMF code. | /usr/share/OVMF/OVMF_CODE.fd |
|
||||
| MANAGER_QEMU_OVMF_VERSION | The version number of EDKII from which OVMF was built | edk2-stable202408 |
|
||||
| MANAGER_QEMU_OVMF_CODE_READONLY | Whether the OVMF code should be read-only. | on |
|
||||
| MANAGER_QEMU_OVMF_VARS_IF | The interface type for the OVMF variables. | pflash |
|
||||
| MANAGER_QEMU_OVMF_VARS_FORMAT | The format of the OVMF variables file. | raw |
|
||||
| MANAGER_QEMU_OVMF_VARS_UNIT | The unit number for the OVMF variables. | 1 |
|
||||
| MANAGER_QEMU_OVMF_VARS_FILE | The file path for the OVMF variables. | /usr/share/OVMF/OVMF_VARS.fd |
|
||||
| MANAGER_QEMU_NETDEV_ID | The ID for the network device. | vmnic |
|
||||
| MANAGER_QEMU_HOST_FWD_AGENT | The port number for the host forward agent. | 7020 |
|
||||
| MANAGER_QEMU_GUEST_FWD_AGENT | The port number for the guest forward agent. | 7002 |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_DISABLE_LEGACY | Whether to disable the legacy PCI device. | on |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_IOMMU_PLATFORM | Whether to enable the IOMMU platform for the virtio-net PCI device. | true |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_ADDR | The PCI address for the virtio-net PCI device. | 0x2 |
|
||||
| MANAGER_QEMU_VIRTIO_NET_PCI_ROMFILE | The file path for the ROM image for the virtio-net PCI device. | |
|
||||
| MANAGER_QEMU_DISK_IMG_KERNEL_FILE | The file path for the kernel image. | img/bzImage |
|
||||
| MANAGER_QEMU_DISK_IMG_ROOTFS_FILE | The file path for the root filesystem image. | img/rootfs.cpio.gz |
|
||||
| MANAGER_QEMU_SEV_SNP_ID | The ID for the Secure Encrypted Virtualization (SEV-SNP) device. | sev0 |
|
||||
| MANAGER_QEMU_SEV_SNP_CBITPOS | The position of the C-bit in the physical address. | 51 |
|
||||
| MANAGER_QEMU_SEV_SNP_REDUCED_PHYS_BITS | The number of reduced physical address bits for SEV-SNP. | 1 |
|
||||
| MANAGER_QEMU_ENABLE_HOST_DATA | Enable additional data for the SEV-SNP host. | false |
|
||||
| MANAGER_QEMU_HOST_DATA | Additional data for the SEV-SNP host. | |
|
||||
| MANAGER_QEMU_TDX_ID | The ID for the Trust Domain Extensions (TDX) device. | tdx0 |
|
||||
| MANAGER_QEMU_QUOTE_GENERATION_PORT | The port number for virtual socket used to communicate with the Quote Generation Service (QGS). | 4050 |
|
||||
| MANAGER_QEMU_OVMF_FILE | The file path for the OVMF file (combined OVMF_CODE and OVMF_VARS file). | /usr/share/ovmf/OVMF.fd |
|
||||
| MANAGER_QEMU_IGVM_ID | The ID of the IGVM file. | igvm0 |
|
||||
| MANAGER_QEMU_IGVM_FILE | The file path to the IGVM file. | /root/coconut-qemu.igvm |
|
||||
| MANAGER_QEMU_BIN_PATH | The file path for the QEMU binary. | qemu-system-x86_64 |
|
||||
| MANAGER_QEMU_USE_SUDO | Whether to use sudo to run QEMU. | false |
|
||||
| MANAGER_QEMU_ENABLE_SEV_SNP | Whether to enable Secure Nested Paging (SEV-SNP). | true |
|
||||
| MANAGER_QEMU_ENABLE_TDX | Whether to enable Trust Domain Extensions (TDX). | false |
|
||||
| MANAGER_QEMU_ENABLE_KVM | Whether to enable the Kernel-based Virtual Machine (KVM) acceleration. | true |
|
||||
| MANAGER_QEMU_MACHINE | The machine type for QEMU. | q35 |
|
||||
| MANAGER_QEMU_CPU | The CPU model for QEMU. | EPYC |
|
||||
| MANAGER_QEMU_SMP_COUNT | The number of virtual CPUs. | 4 |
|
||||
| MANAGER_QEMU_SMP_MAXCPUS | The maximum number of virtual CPUs. | 64 |
|
||||
| MANAGER_QEMU_MEM_ID | The ID for the memory device. | ram1 |
|
||||
| MANAGER_QEMU_NO_GRAPHIC | Whether to disable the graphical display. | true |
|
||||
| MANAGER_QEMU_MONITOR | The type of monitor to use. | pty |
|
||||
| MANAGER_QEMU_HOST_FWD_RANGE | The range of host ports to forward. | 6100-6200 |
|
||||
| MANAGER_MAX_VMS | The maximum number of vms running concurrently on manager. | 10 |
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -90,21 +100,11 @@ sudo apt install qemu-kvm
|
||||
|
||||
Create `img` 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
|
||||
```
|
||||
#### Virtual filesystem
|
||||
|
||||
If `vhost_vsock` is not loaded run the following commands:
|
||||
9P (or Plan 9 Filesystem) in QEMU is a lightweight, network-based file-sharing protocol. In Cocos, the 9P is used to transfer environment variables and TLS certificates for cloud communication from the Manager to the Agent.
|
||||
|
||||
```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
|
||||
```
|
||||
You should define the environment variables in a file called environment. For the number and meaning of the environment variables, please refer to the Agent [Readme](https://github.com/ultravioletrs/cocos/blob/main/agent/README.md).
|
||||
|
||||
### Prepare Cocos HAL
|
||||
|
||||
@@ -124,10 +124,32 @@ OVMF_CODE=/usr/share/OVMF/OVMF_CODE.fd
|
||||
|
||||
sudo find / -name OVMF_VARS.fd
|
||||
# => /usr/share/OVMF/OVMF_VARS.fd
|
||||
OVMF_VARS=/usr/share/OVMF/OVMF_VARS.fd
|
||||
|
||||
# Create a local copy of OVMF_VARS.
|
||||
cp /usr/share/OVMF/OVMF_VARS.fd .
|
||||
|
||||
# Create a directory for the environment file and the certificates for cloud certificates.
|
||||
mkdir env
|
||||
mkdir certs
|
||||
|
||||
# Enter the env directory and create the environemnt file.
|
||||
cd env
|
||||
touch environment
|
||||
|
||||
# Define Computations endpoint URL for agent.
|
||||
# Make sure the Computation endpoint is running (like Cocos Prism).
|
||||
echo AGENT_CVM_GRPC_URL=localhost:7001 >> ./environment
|
||||
# Define log level for the agent.
|
||||
echo AGENT_LOG_LEVEL=debug >> ./environment
|
||||
|
||||
# Return to cmd/manager
|
||||
cd ..
|
||||
|
||||
OVMF_VARS=./OVMF_VARS.fd
|
||||
KERNEL="img/bzImage"
|
||||
INITRD="img/rootfs.cpio.gz"
|
||||
ENV_PATH=./env
|
||||
CERTH_PATH=./certs
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-enable-kvm \
|
||||
@@ -137,15 +159,19 @@ qemu-system-x86_64 \
|
||||
-m 2048M,slots=5,maxmem=10240M \
|
||||
-no-reboot \
|
||||
-drive if=pflash,format=raw,unit=0,file=$OVMF_CODE,readonly=on \
|
||||
-drive if=pflash,format=raw,unit=1,file=$OVMF_VARS \
|
||||
-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" \
|
||||
-initrd $INITRD \
|
||||
-nographic \
|
||||
-monitor pty \
|
||||
-monitor unix:monitor,server,nowait
|
||||
-monitor unix:monitor,server,nowait \
|
||||
-fsdev local,id=env_fs,path=$ENV_PATH,security_model=mapped \
|
||||
-device virtio-9p-pci,fsdev=env_fs,mount_tag=env_share \
|
||||
-fsdev local,id=cert_fs,path=$CERTH_PATH,security_model=mapped \
|
||||
-device virtio-9p-pci,fsdev=cert_fs,mount_tag=certs_share
|
||||
```
|
||||
Once the VM is booted press enter and on the login use username `root`.
|
||||
|
||||
@@ -190,10 +216,21 @@ MANAGER_QEMU_OVMF_VARS_FILE=/usr/share/OVMF/OVMF_VARS.fd
|
||||
|
||||
NB: we set environment variables that we will use in the shell process where we run `manager`.
|
||||
|
||||
### Trusted Platform Module (TPM)
|
||||
|
||||
The Trusted Platform Module (TPM) plays a fundamental role in this process by providing a tamper-resistant foundation for cryptographic operations, securing sensitive artifacts, measuring system state, and enabling attestation mechanisms.
|
||||
|
||||
### IGVM
|
||||
|
||||
An IGVM file contains all the necessary information to launch a virtual machine on different virtualization platforms. It includes setup commands for the guest system and verification data to ensure the VM is loaded securely and correctly.
|
||||
|
||||
Cocos uses the [COCONUT-SVSM](https://github.com/coconut-svsm/svsm/blob/main/Documentation/docs/installation/INSTALL.md) for the vTPM. The IGVM file contains the OVMF file and the vTPM.
|
||||
|
||||
## Deployment
|
||||
|
||||
To start the service, execute the following shell script (note a server needs to be running see [here](../test/cvms/README.md)):
|
||||
To start the service, execute the following shell script (note a server needs to be running see [here](../test/cvms/README.md)):
|
||||
|
||||
The manager can be started as a *systemd* service or a standalone executable. To start the manager as a systemd service, look at the systemd service script [here](https://github.com/ultravioletrs/cocos/blob/main/init/systemd/cocos-manager.service). The environment variables are defined in the `cocos-manager.env` file. Below are examples of how to start the manager.
|
||||
|
||||
```bash
|
||||
# Download the latest version of the service
|
||||
@@ -205,75 +242,39 @@ cd cocos
|
||||
make manager
|
||||
|
||||
# Set the environment variables and run the service
|
||||
MANAGER_GRPC_URL=localhost:7001
|
||||
MANAGER_GRPC_URL=localhost:7001 \
|
||||
MANAGER_LOG_LEVEL=debug \
|
||||
MANAGER_QEMU_USE_SUDO=false \
|
||||
MANAGER_QEMU_ENABLE_SEV=false \
|
||||
./build/cocos-manager
|
||||
```
|
||||
|
||||
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 \
|
||||
MANAGER_QEMU_SEV_CBITPOS=51 \
|
||||
./build/cocos-manager
|
||||
```
|
||||
|
||||
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`.
|
||||
To start SEV-SNP, define the IGVM file that contains the vTPM and the OVMF (combined OVMF_CODE and OVMF_VARS) of the CVM.
|
||||
|
||||
To enable [AMD SEV-SNP](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_ENABLE_SEV=false \
|
||||
MANAGER_QEMU_ENABLE_SEV_SNP=true \
|
||||
MANAGER_QEMU_SEV_CBITPOS=51 \
|
||||
MANAGER_QEMU_SEV_SNP_CBITPOS=51 \
|
||||
MANAGER_QEMU_BIN_PATH=<path to QEMU binary> \
|
||||
MANAGER_QEMU_IGVM_FILE=<path to IGVM file> \
|
||||
./build/cocos-manager
|
||||
```
|
||||
|
||||
### Verifying VM launch
|
||||
|
||||
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:
|
||||
To enable [TDX](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html) support, start manager like this
|
||||
|
||||
```sh
|
||||
ps aux | grep qemu-system-x86_64
|
||||
MANAGER_GRPC_URL=localhost:7001 \
|
||||
MANAGER_LOG_LEVEL=debug \
|
||||
MANAGER_QEMU_ENABLE_SEV_SNP=false \
|
||||
MANAGER_QEMU_ENABLE_TDX=true \
|
||||
MANAGER_QEMU_CPU=host \
|
||||
MANAGER_QEMU_BIN_PATH=<path to QEMU binary> \
|
||||
MANAGER_QEMU_OVMF_FILE=<path to OVMF file> \
|
||||
./build/cocos-manager
|
||||
```
|
||||
|
||||
You should get something similar to this
|
||||
```
|
||||
darko 324763 95.3 6.0 6398136 981044 ? Sl 16:17 0:15 /usr/bin/qemu-system-x86_64 -enable-kvm -machine q35 -cpu EPYC -smp 4,maxcpus=64 -m 4096M,slots=5,maxmem=30G -drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on -drive if=pflash,format=raw,unit=1,file=img/OVMF_VARS.fd -device virtio-scsi-pci,id=scsi,disable-legacy=on,iommu_platform=true -drive file=img/focal-server-cloudimg-amd64.img,if=none,id=disk0,format=qcow2 -device scsi-hd,drive=disk0 -netdev user,id=vmnic,hostfwd=tcp::2222-:22,hostfwd=tcp::9301-:9031,hostfwd=tcp::7020-:7002 -device virtio-net-pci,disable-legacy=on,iommu_platform=true,netdev=vmnic,romfile= -nographic -monitor pty
|
||||
```
|
||||
|
||||
If you run a command as `sudo`, you should get the output similar to this one
|
||||
|
||||
```
|
||||
root 37982 0.0 0.0 9444 4572 pts/0 S+ 16:18 0:00 sudo /usr/local/bin/qemu-system-x86_64 -enable-kvm -machine q35 -cpu EPYC -smp 4,maxcpus=64 -m 4096M,slots=5,maxmem=30G -drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on -drive if=pflash,format=raw,unit=1,file=img/OVMF_VARS.fd -device virtio-scsi-pci,id=scsi,disable-legacy=on,iommu_platform=true -drive file=img/focal-server-cloudimg-amd64.img,if=none,id=disk0,format=qcow2 -device scsi-hd,drive=disk0 -netdev user,id=vmnic,hostfwd=tcp::2222-:22,hostfwd=tcp::9301-:9031,hostfwd=tcp::7020-:7002 -device virtio-net-pci,disable-legacy=on,iommu_platform=true,netdev=vmnic,romfile= -object sev-guest,id=sev0,cbitpos=51,reduced-phys-bits=1 -machine memory-encryption=sev0 -nographic -monitor pty
|
||||
root 37989 122 13.1 5345816 4252312 pts/0 Sl+ 16:19 0:04 /usr/local/bin/qemu-system-x86_64 -enable-kvm -machine q35 -cpu EPYC -smp 4,maxcpus=64 -m 4096M,slots=5,maxmem=30G -drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on -drive if=pflash,format=raw,unit=1,file=img/OVMF_VARS.fd -device virtio-scsi-pci,id=scsi,disable-legacy=on,iommu_platform=true -drive file=img/focal-server-cloudimg-amd64.img,if=none,id=disk0,format=qcow2 -device scsi-hd,drive=disk0 -netdev user,id=vmnic,hostfwd=tcp::2222-:22,hostfwd=tcp::9301-:9031,hostfwd=tcp::7020-:7002 -device virtio-net-pci,disable-legacy=on,iommu_platform=true,netdev=vmnic,romfile= -object sev-guest,id=sev0,cbitpos=51,reduced-phys-bits=1 -machine memory-encryption=sev0 -nographic -monitor pty
|
||||
```
|
||||
|
||||
The two processes are due to the fact that we run the command `/usr/bin/qemu-system-x86_64` as `sudo`, so there is one process for `sudo` command and the other for `/usr/bin/qemu-system-x86_64`.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If the `ps aux | grep qemu-system-x86_64` give you something like this
|
||||
|
||||
@@ -35,22 +35,22 @@ func (s *grpcServer) CreateVm(ctx context.Context, req *manager.CreateReq) (*man
|
||||
|
||||
return &manager.CreateRes{
|
||||
ForwardedPort: port,
|
||||
SvmId: id,
|
||||
CvmId: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) RemoveVm(ctx context.Context, req *manager.RemoveReq) (*emptypb.Empty, error) {
|
||||
if err := s.svc.RemoveVM(ctx, req.SvmId); err != nil {
|
||||
if err := s.svc.RemoveVM(ctx, req.CvmId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *grpcServer) SVMInfo(ctx context.Context, req *manager.SVMInfoReq) (*manager.SVMInfoRes, error) {
|
||||
ovmf, cpunum, cputype, eosversion := s.svc.ReturnSVMInfo(ctx)
|
||||
func (s *grpcServer) CVMInfo(ctx context.Context, req *manager.CVMInfoReq) (*manager.CVMInfoRes, error) {
|
||||
ovmf, cpunum, cputype, eosversion := s.svc.ReturnCVMInfo(ctx)
|
||||
|
||||
return &manager.SVMInfoRes{
|
||||
return &manager.CVMInfoRes{
|
||||
OvmfVersion: ovmf,
|
||||
CpuNum: int32(cpunum),
|
||||
CpuType: cputype,
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/ultravioletrs/cocos/manager"
|
||||
"github.com/ultravioletrs/cocos/manager/mocks"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
assert.NotNil(t, server)
|
||||
assert.IsType(t, &grpcServer{}, server)
|
||||
|
||||
grpcSrv := server.(*grpcServer)
|
||||
assert.Equal(t, mockSvc, grpcSrv.svc)
|
||||
}
|
||||
|
||||
func TestCreateVm(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *manager.CreateReq
|
||||
mockPort string
|
||||
mockId string
|
||||
mockErr error
|
||||
expectedRes *manager.CreateRes
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "successful VM creation",
|
||||
req: &manager.CreateReq{},
|
||||
mockPort: "8080",
|
||||
mockId: "vm-123",
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.CreateRes{
|
||||
ForwardedPort: "8080",
|
||||
CvmId: "vm-123",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "VM creation with different port",
|
||||
req: &manager.CreateReq{},
|
||||
mockPort: "9090",
|
||||
mockId: "vm-456",
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.CreateRes{
|
||||
ForwardedPort: "9090",
|
||||
CvmId: "vm-456",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "VM creation failure",
|
||||
req: &manager.CreateReq{},
|
||||
mockPort: "",
|
||||
mockId: "",
|
||||
mockErr: errors.New("failed to create VM"),
|
||||
expectedRes: nil,
|
||||
expectedErr: errors.New("failed to create VM"),
|
||||
},
|
||||
{
|
||||
name: "VM creation with empty request",
|
||||
req: &manager.CreateReq{},
|
||||
mockPort: "3000",
|
||||
mockId: "vm-empty",
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.CreateRes{
|
||||
ForwardedPort: "3000",
|
||||
CvmId: "vm-empty",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
mockSvc.On("CreateVM", mock.Anything, tt.req).Return(tt.mockPort, tt.mockId, tt.mockErr)
|
||||
|
||||
res, err := server.CreateVm(context.Background(), tt.req)
|
||||
|
||||
if tt.expectedErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr.Error(), err.Error())
|
||||
assert.Nil(t, res)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedRes, res)
|
||||
}
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVm(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *manager.RemoveReq
|
||||
mockErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "successful VM removal",
|
||||
req: &manager.RemoveReq{
|
||||
CvmId: "vm-123",
|
||||
},
|
||||
mockErr: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "VM removal failure",
|
||||
req: &manager.RemoveReq{
|
||||
CvmId: "vm-456",
|
||||
},
|
||||
mockErr: errors.New("VM not found"),
|
||||
expectedErr: errors.New("VM not found"),
|
||||
},
|
||||
{
|
||||
name: "VM removal with empty ID",
|
||||
req: &manager.RemoveReq{
|
||||
CvmId: "",
|
||||
},
|
||||
mockErr: errors.New("invalid VM ID"),
|
||||
expectedErr: errors.New("invalid VM ID"),
|
||||
},
|
||||
{
|
||||
name: "VM removal with non-existent ID",
|
||||
req: &manager.RemoveReq{
|
||||
CvmId: "non-existent-vm",
|
||||
},
|
||||
mockErr: errors.New("VM does not exist"),
|
||||
expectedErr: errors.New("VM does not exist"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
mockSvc.On("RemoveVM", mock.Anything, tt.req.CvmId).Return(tt.mockErr)
|
||||
|
||||
res, err := server.RemoveVm(context.Background(), tt.req)
|
||||
|
||||
if tt.expectedErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr.Error(), err.Error())
|
||||
assert.Nil(t, res)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &emptypb.Empty{}, res)
|
||||
}
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCVMInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *manager.CVMInfoReq
|
||||
mockOvmf string
|
||||
mockCpuNum int
|
||||
mockCpuType string
|
||||
mockEosVersion string
|
||||
expectedRes *manager.CVMInfoRes
|
||||
}{
|
||||
{
|
||||
name: "successful CVM info retrieval",
|
||||
req: &manager.CVMInfoReq{
|
||||
Id: "cvm-123",
|
||||
},
|
||||
mockOvmf: "OVMF-v1.0",
|
||||
mockCpuNum: 4,
|
||||
mockCpuType: "Intel-x86_64",
|
||||
mockEosVersion: "EOS-v2.1",
|
||||
expectedRes: &manager.CVMInfoRes{
|
||||
OvmfVersion: "OVMF-v1.0",
|
||||
CpuNum: 4,
|
||||
CpuType: "Intel-x86_64",
|
||||
EosVersion: "EOS-v2.1",
|
||||
Id: "cvm-123",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CVM info with different values",
|
||||
req: &manager.CVMInfoReq{
|
||||
Id: "cvm-456",
|
||||
},
|
||||
mockOvmf: "OVMF-v2.0",
|
||||
mockCpuNum: 8,
|
||||
mockCpuType: "AMD-x86_64",
|
||||
mockEosVersion: "EOS-v3.0",
|
||||
expectedRes: &manager.CVMInfoRes{
|
||||
OvmfVersion: "OVMF-v2.0",
|
||||
CpuNum: 8,
|
||||
CpuType: "AMD-x86_64",
|
||||
EosVersion: "EOS-v3.0",
|
||||
Id: "cvm-456",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CVM info with empty ID",
|
||||
req: &manager.CVMInfoReq{
|
||||
Id: "",
|
||||
},
|
||||
mockOvmf: "OVMF-v1.5",
|
||||
mockCpuNum: 2,
|
||||
mockCpuType: "ARM64",
|
||||
mockEosVersion: "EOS-v1.8",
|
||||
expectedRes: &manager.CVMInfoRes{
|
||||
OvmfVersion: "OVMF-v1.5",
|
||||
CpuNum: 2,
|
||||
CpuType: "ARM64",
|
||||
EosVersion: "EOS-v1.8",
|
||||
Id: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CVM info with zero CPU count",
|
||||
req: &manager.CVMInfoReq{
|
||||
Id: "cvm-zero",
|
||||
},
|
||||
mockOvmf: "OVMF-v1.0",
|
||||
mockCpuNum: 0,
|
||||
mockCpuType: "Unknown",
|
||||
mockEosVersion: "EOS-v1.0",
|
||||
expectedRes: &manager.CVMInfoRes{
|
||||
OvmfVersion: "OVMF-v1.0",
|
||||
CpuNum: 0,
|
||||
CpuType: "Unknown",
|
||||
EosVersion: "EOS-v1.0",
|
||||
Id: "cvm-zero",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
mockSvc.On("ReturnCVMInfo", mock.Anything).Return(
|
||||
tt.mockOvmf, tt.mockCpuNum, tt.mockCpuType, tt.mockEosVersion)
|
||||
|
||||
res, err := server.CVMInfo(context.Background(), tt.req)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedRes, res)
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationPolicy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *manager.AttestationPolicyReq
|
||||
mockPolicy string
|
||||
mockErr error
|
||||
expectedRes *manager.AttestationPolicyRes
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "successful attestation policy fetch",
|
||||
req: &manager.AttestationPolicyReq{
|
||||
Id: "policy-123",
|
||||
},
|
||||
mockPolicy: `{"version": "1.0", "rules": ["rule1", "rule2"]}`,
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.AttestationPolicyRes{
|
||||
Info: []byte(`{"version": "1.0", "rules": ["rule1", "rule2"]}`),
|
||||
Id: "policy-123",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "attestation policy fetch failure",
|
||||
req: &manager.AttestationPolicyReq{
|
||||
Id: "policy-456",
|
||||
},
|
||||
mockPolicy: "",
|
||||
mockErr: errors.New("policy not found"),
|
||||
expectedRes: nil,
|
||||
expectedErr: errors.New("policy not found"),
|
||||
},
|
||||
{
|
||||
name: "attestation policy with empty ID",
|
||||
req: &manager.AttestationPolicyReq{
|
||||
Id: "",
|
||||
},
|
||||
mockPolicy: "",
|
||||
mockErr: errors.New("invalid policy ID"),
|
||||
expectedRes: nil,
|
||||
expectedErr: errors.New("invalid policy ID"),
|
||||
},
|
||||
{
|
||||
name: "attestation policy with different content",
|
||||
req: &manager.AttestationPolicyReq{
|
||||
Id: "policy-789",
|
||||
},
|
||||
mockPolicy: `{"version": "2.0", "attestation_type": "SGX"}`,
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.AttestationPolicyRes{
|
||||
Info: []byte(`{"version": "2.0", "attestation_type": "SGX"}`),
|
||||
Id: "policy-789",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "attestation policy with empty policy content",
|
||||
req: &manager.AttestationPolicyReq{
|
||||
Id: "policy-empty",
|
||||
},
|
||||
mockPolicy: "",
|
||||
mockErr: nil,
|
||||
expectedRes: &manager.AttestationPolicyRes{
|
||||
Info: []byte{},
|
||||
Id: "policy-empty",
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
mockSvc.On("FetchAttestationPolicy", mock.Anything, tt.req.Id).Return([]byte(tt.mockPolicy), tt.mockErr)
|
||||
|
||||
res, err := server.AttestationPolicy(context.Background(), tt.req)
|
||||
|
||||
if tt.expectedErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr.Error(), err.Error())
|
||||
assert.Nil(t, res)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedRes, res)
|
||||
}
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextCancellation(t *testing.T) {
|
||||
t.Run("CreateVm with cancelled context", func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // Cancel the context immediately
|
||||
|
||||
req := &manager.CreateReq{}
|
||||
mockSvc.On("CreateVM", mock.Anything, req).Return("", "", context.Canceled)
|
||||
|
||||
res, err := server.CreateVm(ctx, req)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, context.Canceled, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("RemoveVm with cancelled context", func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // Cancel the context immediately
|
||||
|
||||
req := &manager.RemoveReq{CvmId: "vm-123"}
|
||||
mockSvc.On("RemoveVM", mock.Anything, "vm-123").Return(context.Canceled)
|
||||
|
||||
res, err := server.RemoveVm(ctx, req)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, context.Canceled, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorHandling(t *testing.T) {
|
||||
t.Run("service returns multiple error types", func(t *testing.T) {
|
||||
mockSvc := new(mocks.Service)
|
||||
server := NewServer(mockSvc)
|
||||
|
||||
// Test with different error types
|
||||
customErr := errors.New("custom service error")
|
||||
|
||||
req := &manager.CreateReq{}
|
||||
mockSvc.On("CreateVM", mock.Anything, req).Return("", "", customErr)
|
||||
|
||||
res, err := server.CreateVm(context.Background(), req)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, customErr, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
mockSvc.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/absmach/supermq"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(r *chi.Mux, svcName, instanceID string) http.Handler {
|
||||
r.Get("/health", supermq.Health(svcName, instanceID))
|
||||
r.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMakeHandler(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serviceName string
|
||||
instanceID string
|
||||
expectedRoutes int
|
||||
}{
|
||||
{
|
||||
name: "valid handler creation",
|
||||
serviceName: testServiceName,
|
||||
instanceID: testInstanceID,
|
||||
expectedRoutes: 2, // /health and /metrics
|
||||
},
|
||||
{
|
||||
name: "empty service name",
|
||||
serviceName: "",
|
||||
instanceID: testInstanceID,
|
||||
expectedRoutes: 2,
|
||||
},
|
||||
{
|
||||
name: "empty instance ID",
|
||||
serviceName: testServiceName,
|
||||
instanceID: "",
|
||||
expectedRoutes: 2,
|
||||
},
|
||||
{
|
||||
name: "both empty",
|
||||
serviceName: "",
|
||||
instanceID: "",
|
||||
expectedRoutes: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
handler := MakeHandler(r, tt.serviceName, tt.instanceID)
|
||||
|
||||
require.NotNil(t, handler)
|
||||
assert.Implements(t, (*http.Handler)(nil), handler)
|
||||
|
||||
// Verify that the handler is actually the chi router
|
||||
assert.Equal(t, r, handler)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthEndpoint(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serviceName string
|
||||
instanceID string
|
||||
method string
|
||||
path string
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "GET health endpoint success",
|
||||
serviceName: testServiceName,
|
||||
instanceID: testInstanceID,
|
||||
method: http.MethodGet,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "POST health endpoint not allowed",
|
||||
serviceName: testServiceName,
|
||||
instanceID: testInstanceID,
|
||||
method: http.MethodPost,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusMethodNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "PUT health endpoint not allowed",
|
||||
serviceName: testServiceName,
|
||||
instanceID: testInstanceID,
|
||||
method: http.MethodPut,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusMethodNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "DELETE health endpoint not allowed",
|
||||
serviceName: testServiceName,
|
||||
instanceID: testInstanceID,
|
||||
method: http.MethodDelete,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusMethodNotAllowed,
|
||||
},
|
||||
{
|
||||
name: "health with empty service name",
|
||||
serviceName: "",
|
||||
instanceID: testInstanceID,
|
||||
method: http.MethodGet,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "health with empty instance ID",
|
||||
serviceName: testServiceName,
|
||||
instanceID: "",
|
||||
method: http.MethodGet,
|
||||
path: "/health",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
handler := MakeHandler(r, tt.serviceName, tt.instanceID)
|
||||
|
||||
req, err := http.NewRequest(tt.method, tt.path, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, rr.Code)
|
||||
|
||||
if tt.expectedStatus == http.StatusOK {
|
||||
// Verify content type for successful health checks
|
||||
contentType := rr.Header().Get("Content-Type")
|
||||
assert.Contains(t, contentType, "application/health+json")
|
||||
|
||||
// Verify response body contains service info
|
||||
body := rr.Body.String()
|
||||
if tt.serviceName != "" {
|
||||
assert.Contains(t, body, tt.serviceName)
|
||||
}
|
||||
if tt.instanceID != "" {
|
||||
assert.Contains(t, body, tt.instanceID)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetricsEndpoint(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
method string
|
||||
path string
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "GET metrics endpoint success",
|
||||
method: http.MethodGet,
|
||||
path: "/metrics",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "POST metrics endpoint not allowed",
|
||||
method: http.MethodPost,
|
||||
path: "/metrics",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "PUT metrics endpoint not allowed",
|
||||
method: http.MethodPut,
|
||||
path: "/metrics",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "DELETE metrics endpoint not allowed",
|
||||
method: http.MethodDelete,
|
||||
path: "/metrics",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
handler := MakeHandler(r, testServiceName, testInstanceID)
|
||||
|
||||
req, err := http.NewRequest(tt.method, tt.path, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, rr.Code)
|
||||
|
||||
if tt.expectedStatus == http.StatusOK {
|
||||
// Verify content type for Prometheus metrics
|
||||
contentType := rr.Header().Get("Content-Type")
|
||||
assert.Contains(t, contentType, "text/plain")
|
||||
|
||||
// Verify response contains Prometheus metrics format
|
||||
body := rr.Body.String()
|
||||
assert.Contains(t, body, "# HELP")
|
||||
assert.Contains(t, body, "# TYPE")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotFoundEndpoint(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "root path not found",
|
||||
path: "/",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "random path not found",
|
||||
path: "/random-path",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "health typo not found",
|
||||
path: "/helth",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "metrics typo not found",
|
||||
path: "/metric",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "health with trailing slash",
|
||||
path: "/health/",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "metrics with trailing slash",
|
||||
path: "/metrics/",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
handler := MakeHandler(r, testServiceName, testInstanceID)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, tt.path, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, rr.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentRequests(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
numRequests = 100
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
handler := MakeHandler(r, testServiceName, testInstanceID)
|
||||
|
||||
// Test concurrent health requests
|
||||
t.Run("concurrent health requests", func(t *testing.T) {
|
||||
results := make(chan int, numRequests)
|
||||
|
||||
for i := 0; i < numRequests; i++ {
|
||||
go func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "/health", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
results <- rr.Code
|
||||
}()
|
||||
}
|
||||
|
||||
// Collect all results
|
||||
for i := 0; i < numRequests; i++ {
|
||||
status := <-results
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
}
|
||||
})
|
||||
|
||||
// Test concurrent metrics requests
|
||||
t.Run("concurrent metrics requests", func(t *testing.T) {
|
||||
results := make(chan int, numRequests)
|
||||
|
||||
for i := 0; i < numRequests; i++ {
|
||||
go func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
results <- rr.Code
|
||||
}()
|
||||
}
|
||||
|
||||
// Collect all results
|
||||
for i := 0; i < numRequests; i++ {
|
||||
status := <-results
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandlerWithCustomRouter(t *testing.T) {
|
||||
const (
|
||||
testServiceName = "test-service"
|
||||
testInstanceID = "test-instance-123"
|
||||
)
|
||||
|
||||
// Test with a router that already has some routes
|
||||
r := chi.NewRouter()
|
||||
r.Get("/existing", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if _, err := w.Write([]byte("existing")); err != nil {
|
||||
http.Error(w, "Failed to write response", http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
|
||||
handler := MakeHandler(r, testServiceName, testInstanceID)
|
||||
|
||||
// Test that existing route still works
|
||||
req, err := http.NewRequest(http.MethodGet, "/existing", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, "existing", rr.Body.String())
|
||||
|
||||
// Test that new routes work
|
||||
req, err = http.NewRequest(http.MethodGet, "/health", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
}
|
||||
+20
-3
@@ -67,11 +67,28 @@ func (lm *loggingMiddleware) FetchAttestationPolicy(ctx context.Context, cmpId s
|
||||
return lm.svc.FetchAttestationPolicy(ctx, cmpId)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ReturnSVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
func (lm *loggingMiddleware) ReturnCVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method ReturnSVMInfo for computation took %s to complete", time.Since(begin))
|
||||
message := fmt.Sprintf("Method ReturnCVMInfo for computation took %s to complete", time.Since(begin))
|
||||
lm.logger.Info(message)
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ReturnSVMInfo(ctx)
|
||||
return lm.svc.ReturnCVMInfo(ctx)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Shutdown() (err error) {
|
||||
defer func(begin time.Time) {
|
||||
if err != nil {
|
||||
lm.logger.Warn("Method Shutdown completed with error",
|
||||
"time_taken", time.Since(begin),
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Method Shutdown completed successfully",
|
||||
"time_taken", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Shutdown()
|
||||
}
|
||||
|
||||
+13
-4
@@ -59,11 +59,20 @@ func (ms *metricsMiddleware) FetchAttestationPolicy(ctx context.Context, cmpId s
|
||||
return ms.svc.FetchAttestationPolicy(ctx, cmpId)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ReturnSVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
func (ms *metricsMiddleware) ReturnCVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "ReturnSVMInfo").Add(1)
|
||||
ms.latency.With("method", "ReturnSVMInfo").Observe(time.Since(begin).Seconds())
|
||||
ms.counter.With("method", "ReturnCVMInfo").Add(1)
|
||||
ms.latency.With("method", "ReturnCVMInfo").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ReturnSVMInfo(ctx)
|
||||
return ms.svc.ReturnCVMInfo(ctx)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Shutdown() error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "Shutdown").Add(1)
|
||||
ms.latency.With("method", "Shutdown").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Shutdown()
|
||||
}
|
||||
|
||||
+109
-51
@@ -11,25 +11,18 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/ultravioletrs/cocos/manager/qemu"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/igvmmeasure"
|
||||
"github.com/virtee/sev-snp-measure-go/cpuid"
|
||||
"github.com/virtee/sev-snp-measure-go/guest"
|
||||
"github.com/virtee/sev-snp-measure-go/vmmtypes"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/cmdconfig"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
const defGuestFeatures = 0x1
|
||||
|
||||
func (ms *managerService) FetchAttestationPolicy(_ context.Context, computationId string) ([]byte, error) {
|
||||
cmd := exec.Command("sudo", fmt.Sprintf("%s/attestation_policy", ms.attestationPolicyBinaryPath), "--policy", "196608")
|
||||
|
||||
ms.mu.Lock()
|
||||
vm, exists := ms.vms[computationId]
|
||||
ms.mu.Unlock()
|
||||
@@ -42,70 +35,135 @@ func (ms *managerService) FetchAttestationPolicy(_ context.Context, computationI
|
||||
return nil, fmt.Errorf("failed to cast config to qemu.VMInfo")
|
||||
}
|
||||
|
||||
ms.ap.Lock()
|
||||
_, err := cmd.Output()
|
||||
ms.ap.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ms.ap.Lock()
|
||||
f, err := os.ReadFile("./attestation_policy.json")
|
||||
ms.ap.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var attestationPolicy check.Config
|
||||
|
||||
if err = protojson.Unmarshal(f, &attestationPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var measurement []byte
|
||||
var attestPolicyCmd *cmdconfig.CmdConfig
|
||||
var err error
|
||||
switch {
|
||||
case vmi.Config.EnableSEV:
|
||||
measurement, err = guest.CalcLaunchDigest(guest.SEV, vmi.Config.SMPCount, uint64(cpuid.CpuSigs[ms.qemuCfg.CPU]), vmi.Config.OVMFCodeConfig.File, vmi.Config.KernelFile, vmi.Config.RootFsFile, strconv.Quote(qemu.KernelCommandLine), defGuestFeatures, "", vmmtypes.QEMU, false, "", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case vmi.Config.EnableSEVSNP:
|
||||
igvmMeasurementBinaryPath := fmt.Sprintf("%s/igvmmeasure", ms.attestationPolicyBinaryPath)
|
||||
attestPolicyCmd, err = fetchSNPAttestationPolicy(ms)
|
||||
case vmi.Config.EnableTDX:
|
||||
attestPolicyCmd, err = fetchTDXAttestationPolicy(ms)
|
||||
}
|
||||
|
||||
var stdoutBuffer bytes.Buffer
|
||||
var stderrBuffer bytes.Buffer
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdout := bufio.NewWriter(&stdoutBuffer)
|
||||
var stdOutByte []byte
|
||||
ms.ap.Lock()
|
||||
switch {
|
||||
case vmi.Config.EnableSEVSNP:
|
||||
stdOutByte, err = attestPolicyCmd.Run(ms.attestationPolicyBinaryPath)
|
||||
case vmi.Config.EnableTDX:
|
||||
stdOutByte, err = attestPolicyCmd.Run("")
|
||||
}
|
||||
ms.ap.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var policy []byte
|
||||
switch {
|
||||
case vmi.Config.EnableSEVSNP:
|
||||
policy, err = readSEVSNPPolicy(stdOutByte, ms, vmi)
|
||||
case vmi.Config.EnableTDX:
|
||||
policy = stdOutByte
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func fetchSNPAttestationPolicy(ms *managerService) (*cmdconfig.CmdConfig, error) {
|
||||
var stderrBuffer bytes.Buffer
|
||||
options := []string{"--policy", "196608"}
|
||||
|
||||
if ms.pcrValuesFilePath != "" {
|
||||
pcrValues := []string{"--pcr", ms.pcrValuesFilePath}
|
||||
options = append(options, pcrValues...)
|
||||
}
|
||||
|
||||
stderr := bufio.NewWriter(&stderrBuffer)
|
||||
|
||||
attestPolicyCmd, err := cmdconfig.NewCmdConfig("sudo", options, stderr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return attestPolicyCmd, nil
|
||||
}
|
||||
|
||||
func fetchTDXAttestationPolicy(ms *managerService) (*cmdconfig.CmdConfig, error) {
|
||||
var stderrBuffer bytes.Buffer
|
||||
|
||||
stderr := bufio.NewWriter(&stderrBuffer)
|
||||
|
||||
attestPolicyCmd, err := cmdconfig.NewCmdConfig(ms.attestationPolicyBinaryPath, nil, stderr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return attestPolicyCmd, nil
|
||||
}
|
||||
|
||||
func readSEVSNPPolicy(stdOutByte []byte, ms *managerService, vmi qemu.VMInfo) ([]byte, error) {
|
||||
attestationPolicy := attestation.Config{Config: &check.Config{RootOfTrust: &check.RootOfTrust{}, Policy: &check.Policy{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
|
||||
if err := vtpm.ReadPolicyFromByte(stdOutByte, &attestationPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stderrBuffer bytes.Buffer
|
||||
var measurement []byte
|
||||
var err error
|
||||
switch {
|
||||
case vmi.Config.EnableSEVSNP:
|
||||
stderr := bufio.NewWriter(&stderrBuffer)
|
||||
options := cmdconfig.IgvmMeasureOptions
|
||||
|
||||
igvmMeasurement, err := igvmmeasure.NewIgvmMeasurement(igvmMeasurementBinaryPath, stderr, stdout)
|
||||
igvmMeasurement, err := cmdconfig.NewCmdConfig(ms.igvmMeasurementBinaryPath, options, stderr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = igvmMeasurement.Run(ms.qemuCfg.IGVMConfig.File)
|
||||
outputByte, err := igvmMeasurement.Run(ms.qemuCfg.IGVMConfig.File)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
measurement = stdoutBuffer.Bytes()
|
||||
outputString := string(outputByte)
|
||||
lines := strings.Split(strings.TrimSpace(outputString), "\n")
|
||||
|
||||
if len(lines) == 1 {
|
||||
outputString = strings.TrimSpace(outputString)
|
||||
outputString = strings.ToLower(outputString)
|
||||
} else {
|
||||
return nil, fmt.Errorf("error: %s", outputString)
|
||||
}
|
||||
|
||||
measurement, err = hex.DecodeString(outputString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if measurement != nil {
|
||||
attestationPolicy.Policy.Measurement = measurement
|
||||
attestationPolicy.Config.Policy.Measurement = measurement
|
||||
}
|
||||
|
||||
if vmi.Config.SevConfig.EnableHostData {
|
||||
hostData, err := base64.StdEncoding.DecodeString(vmi.Config.SevConfig.HostData)
|
||||
if vmi.Config.SEVSNPConfig.EnableHostData {
|
||||
hostData, err := base64.StdEncoding.DecodeString(vmi.Config.SEVSNPConfig.HostData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attestationPolicy.Policy.HostData = hostData
|
||||
attestationPolicy.Config.Policy.HostData = hostData
|
||||
}
|
||||
|
||||
attestationPolicy.Policy.MinimumLaunchTcb = vmi.LaunchTCB
|
||||
attestationPolicy.Config.Policy.MinimumLaunchTcb = vmi.LaunchTCB
|
||||
|
||||
f, err = protojson.Marshal(&attestationPolicy)
|
||||
f, err := vtpm.ConvertPolicyToJSON(&attestationPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -20,7 +21,7 @@ func CreateDummyAttestationPolicyBinary(t *testing.T, behavior string) string {
|
||||
switch behavior {
|
||||
case "success":
|
||||
content = []byte(`#!/bin/sh
|
||||
echo '{"policy": {"measurement": null, "host_data": null}}' > attestation_policy.json
|
||||
echo '{"pcr_values": {"sha256": null, "sha384": null}, "policy": {"measurement": null, "host_data": null}}'
|
||||
`)
|
||||
case "fail":
|
||||
content = []byte(`#!/bin/sh
|
||||
@@ -52,22 +53,18 @@ func TestFetchAttestationPolicy(t *testing.T) {
|
||||
expectedResult map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "Valid SEV configuration",
|
||||
computationId: "sev-computation",
|
||||
name: "Valid SEV-SNP configuration",
|
||||
computationId: "sev-snp-computation",
|
||||
binaryBehavior: "success",
|
||||
vmConfig: qemu.VMInfo{
|
||||
Config: qemu.Config{
|
||||
EnableSEV: true,
|
||||
EnableSEVSNP: false,
|
||||
EnableSEVSNP: true,
|
||||
SMPCount: 2,
|
||||
CPU: "EPYC",
|
||||
OVMFCodeConfig: qemu.OVMFCodeConfig{
|
||||
File: "/path/to/OVMF_CODE.fd",
|
||||
},
|
||||
},
|
||||
LaunchTCB: 0,
|
||||
},
|
||||
expectedError: "open /path/to/OVMF_CODE.fd: no such file or directory",
|
||||
expectedError: "pathToBinary cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "Invalid computation ID",
|
||||
@@ -89,7 +86,7 @@ func TestFetchAttestationPolicy(t *testing.T) {
|
||||
binaryBehavior: "fail",
|
||||
vmConfig: qemu.VMInfo{
|
||||
Config: qemu.Config{
|
||||
EnableSEV: true,
|
||||
EnableSEVSNP: true,
|
||||
},
|
||||
LaunchTCB: 0,
|
||||
},
|
||||
@@ -101,11 +98,11 @@ func TestFetchAttestationPolicy(t *testing.T) {
|
||||
binaryBehavior: "no_json",
|
||||
vmConfig: qemu.VMInfo{
|
||||
Config: qemu.Config{
|
||||
EnableSEV: true,
|
||||
EnableSEVSNP: true,
|
||||
},
|
||||
LaunchTCB: 0,
|
||||
},
|
||||
expectedError: "no such file or directory",
|
||||
expectedError: "failed to decode Attestation Policy file",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -116,7 +113,8 @@ func TestFetchAttestationPolicy(t *testing.T) {
|
||||
|
||||
ms := &managerService{
|
||||
vms: make(map[string]vm.VM),
|
||||
attestationPolicyBinaryPath: tempDir,
|
||||
attestationPolicyBinaryPath: path.Join(tempDir, "attestation_policy"),
|
||||
pcrValuesFilePath: tempDir,
|
||||
qemuCfg: qemu.Config{
|
||||
CPU: "EPYC",
|
||||
},
|
||||
|
||||
+100
-80
@@ -3,8 +3,8 @@
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.4
|
||||
// protoc v5.29.3
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc v5.29.0
|
||||
// source: manager/manager.proto
|
||||
|
||||
package manager
|
||||
@@ -32,6 +32,8 @@ type CreateReq struct {
|
||||
AgentCvmClientKey []byte `protobuf:"bytes,3,opt,name=agent_cvm_client_key,json=agentCvmClientKey,proto3" json:"agent_cvm_client_key,omitempty"`
|
||||
AgentCvmClientCert []byte `protobuf:"bytes,4,opt,name=agent_cvm_client_cert,json=agentCvmClientCert,proto3" json:"agent_cvm_client_cert,omitempty"`
|
||||
AgentCvmServerUrl string `protobuf:"bytes,5,opt,name=agent_cvm_server_url,json=agentCvmServerUrl,proto3" json:"agent_cvm_server_url,omitempty"`
|
||||
AgentCvmCaUrl string `protobuf:"bytes,6,opt,name=agent_cvm_ca_url,json=agentCvmCaUrl,proto3" json:"agent_cvm_ca_url,omitempty"`
|
||||
Ttl string `protobuf:"bytes,7,opt,name=ttl,proto3" json:"ttl,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -101,10 +103,24 @@ func (x *CreateReq) GetAgentCvmServerUrl() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateReq) GetAgentCvmCaUrl() string {
|
||||
if x != nil {
|
||||
return x.AgentCvmCaUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateReq) GetTtl() string {
|
||||
if x != nil {
|
||||
return x.Ttl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CreateRes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ForwardedPort string `protobuf:"bytes,1,opt,name=forwarded_port,json=forwardedPort,proto3" json:"forwarded_port,omitempty"`
|
||||
SvmId string `protobuf:"bytes,2,opt,name=svm_id,json=svmId,proto3" json:"svm_id,omitempty"`
|
||||
CvmId string `protobuf:"bytes,2,opt,name=cvm_id,json=cvmId,proto3" json:"cvm_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -146,16 +162,16 @@ func (x *CreateRes) GetForwardedPort() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateRes) GetSvmId() string {
|
||||
func (x *CreateRes) GetCvmId() string {
|
||||
if x != nil {
|
||||
return x.SvmId
|
||||
return x.CvmId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RemoveReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
SvmId string `protobuf:"bytes,1,opt,name=svm_id,json=svmId,proto3" json:"svm_id,omitempty"`
|
||||
CvmId string `protobuf:"bytes,1,opt,name=cvm_id,json=cvmId,proto3" json:"cvm_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -190,9 +206,9 @@ func (*RemoveReq) Descriptor() ([]byte, []int) {
|
||||
return file_manager_manager_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *RemoveReq) GetSvmId() string {
|
||||
func (x *RemoveReq) GetCvmId() string {
|
||||
if x != nil {
|
||||
return x.SvmId
|
||||
return x.CvmId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -249,7 +265,7 @@ func (x *AttestationPolicyRes) GetId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type SVMInfoRes struct {
|
||||
type CVMInfoRes struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
OvmfVersion string `protobuf:"bytes,2,opt,name=ovmf_version,json=ovmfVersion,proto3" json:"ovmf_version,omitempty"`
|
||||
@@ -261,20 +277,20 @@ type SVMInfoRes struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) Reset() {
|
||||
*x = SVMInfoRes{}
|
||||
func (x *CVMInfoRes) Reset() {
|
||||
*x = CVMInfoRes{}
|
||||
mi := &file_manager_manager_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) String() string {
|
||||
func (x *CVMInfoRes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SVMInfoRes) ProtoMessage() {}
|
||||
func (*CVMInfoRes) ProtoMessage() {}
|
||||
|
||||
func (x *SVMInfoRes) ProtoReflect() protoreflect.Message {
|
||||
func (x *CVMInfoRes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_manager_manager_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -286,47 +302,47 @@ func (x *SVMInfoRes) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SVMInfoRes.ProtoReflect.Descriptor instead.
|
||||
func (*SVMInfoRes) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use CVMInfoRes.ProtoReflect.Descriptor instead.
|
||||
func (*CVMInfoRes) Descriptor() ([]byte, []int) {
|
||||
return file_manager_manager_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetId() string {
|
||||
func (x *CVMInfoRes) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetOvmfVersion() string {
|
||||
func (x *CVMInfoRes) GetOvmfVersion() string {
|
||||
if x != nil {
|
||||
return x.OvmfVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetCpuNum() int32 {
|
||||
func (x *CVMInfoRes) GetCpuNum() int32 {
|
||||
if x != nil {
|
||||
return x.CpuNum
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetCpuType() string {
|
||||
func (x *CVMInfoRes) GetCpuType() string {
|
||||
if x != nil {
|
||||
return x.CpuType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetKernelCmd() string {
|
||||
func (x *CVMInfoRes) GetKernelCmd() string {
|
||||
if x != nil {
|
||||
return x.KernelCmd
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SVMInfoRes) GetEosVersion() string {
|
||||
func (x *CVMInfoRes) GetEosVersion() string {
|
||||
if x != nil {
|
||||
return x.EosVersion
|
||||
}
|
||||
@@ -377,27 +393,27 @@ func (x *AttestationPolicyReq) GetId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type SVMInfoReq struct {
|
||||
type CVMInfoReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SVMInfoReq) Reset() {
|
||||
*x = SVMInfoReq{}
|
||||
func (x *CVMInfoReq) Reset() {
|
||||
*x = CVMInfoReq{}
|
||||
mi := &file_manager_manager_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SVMInfoReq) String() string {
|
||||
func (x *CVMInfoReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SVMInfoReq) ProtoMessage() {}
|
||||
func (*CVMInfoReq) ProtoMessage() {}
|
||||
|
||||
func (x *SVMInfoReq) ProtoReflect() protoreflect.Message {
|
||||
func (x *CVMInfoReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_manager_manager_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -409,12 +425,12 @@ func (x *SVMInfoReq) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SVMInfoReq.ProtoReflect.Descriptor instead.
|
||||
func (*SVMInfoReq) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use CVMInfoReq.ProtoReflect.Descriptor instead.
|
||||
func (*CVMInfoReq) Descriptor() ([]byte, []int) {
|
||||
return file_manager_manager_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *SVMInfoReq) GetId() string {
|
||||
func (x *CVMInfoReq) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
@@ -427,7 +443,7 @@ var file_manager_manager_proto_rawDesc = string([]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, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x80, 0x02,
|
||||
0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x02,
|
||||
0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0f, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65,
|
||||
@@ -444,51 +460,55 @@ var file_manager_manager_proto_rawDesc = string([]byte{
|
||||
0x2f, 0x0a, 0x14, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x76, 0x6d, 0x5f, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x43, 0x76, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c,
|
||||
0x22, 0x49, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x12, 0x25, 0x0a,
|
||||
0x0e, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64,
|
||||
0x50, 0x6f, 0x72, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x76, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x76, 0x6d, 0x49, 0x64, 0x22, 0x22, 0x0a, 0x09, 0x52,
|
||||
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x76, 0x6d, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x76, 0x6d, 0x49, 0x64, 0x22,
|
||||
0x3a, 0x0a, 0x14, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f,
|
||||
0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x0a,
|
||||
0x53, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x76,
|
||||
0x6d, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0b, 0x6f, 0x76, 0x6d, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a,
|
||||
0x07, 0x63, 0x70, 0x75, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06,
|
||||
0x63, 0x70, 0x75, 0x4e, 0x75, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x70, 0x75, 0x54, 0x79, 0x70,
|
||||
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6d, 0x64, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x43, 0x6d, 0x64,
|
||||
0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x22, 0x26, 0x0a, 0x14, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x0a, 0x53, 0x56, 0x4d,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0x8c, 0x02, 0x0a, 0x0e, 0x4d, 0x61, 0x6e, 0x61,
|
||||
0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x43, 0x72,
|
||||
0x65, 0x61, 0x74, 0x65, 0x56, 0x6d, 0x12, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
|
||||
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6d, 0x61, 0x6e,
|
||||
0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x22, 0x00,
|
||||
0x12, 0x38, 0x0a, 0x08, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x56, 0x6d, 0x12, 0x12, 0x2e, 0x6d,
|
||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71,
|
||||
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x07, 0x53, 0x56,
|
||||
0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x13, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
0x53, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x6d, 0x61, 0x6e,
|
||||
0x61, 0x67, 0x65, 0x72, 0x2e, 0x53, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x22,
|
||||
0x00, 0x12, 0x53, 0x0a, 0x11, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
|
||||
0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
|
||||
0x63, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
0x12, 0x27, 0x0a, 0x10, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x76, 0x6d, 0x5f, 0x63, 0x61,
|
||||
0x5f, 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x67, 0x65, 0x6e,
|
||||
0x74, 0x43, 0x76, 0x6d, 0x43, 0x61, 0x55, 0x72, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x49, 0x0a, 0x09, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x77,
|
||||
0x61, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12,
|
||||
0x15, 0x0a, 0x06, 0x63, 0x76, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x63, 0x76, 0x6d, 0x49, 0x64, 0x22, 0x22, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
|
||||
0x52, 0x65, 0x71, 0x12, 0x15, 0x0a, 0x06, 0x63, 0x76, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x76, 0x6d, 0x49, 0x64, 0x22, 0x3a, 0x0a, 0x14, 0x41, 0x74,
|
||||
0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
|
||||
0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x0a, 0x43, 0x56, 0x4d, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x76, 0x6d, 0x66, 0x5f, 0x76, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x76, 0x6d,
|
||||
0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x70, 0x75, 0x5f,
|
||||
0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x70, 0x75, 0x4e, 0x75,
|
||||
0x6d, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x70, 0x75, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
|
||||
0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6d, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x09, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x43, 0x6d, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65,
|
||||
0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0a, 0x65, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x14,
|
||||
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x6d, 0x61, 0x6e, 0x61,
|
||||
0x67, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x79, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x0a, 0x43, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
|
||||
0x69, 0x64, 0x32, 0x8c, 0x02, 0x0a, 0x0e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x53, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56,
|
||||
0x6d, 0x12, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x08, 0x52,
|
||||
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x56, 0x6d, 0x12, 0x12, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
0x72, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x07, 0x43, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x12, 0x13, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x56, 0x4d, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e,
|
||||
0x43, 0x56, 0x4d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x11,
|
||||
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x74, 0x74, 0x65,
|
||||
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71,
|
||||
0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x22,
|
||||
0x00, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
@@ -509,19 +529,19 @@ var file_manager_manager_proto_goTypes = []any{
|
||||
(*CreateRes)(nil), // 1: manager.CreateRes
|
||||
(*RemoveReq)(nil), // 2: manager.RemoveReq
|
||||
(*AttestationPolicyRes)(nil), // 3: manager.AttestationPolicyRes
|
||||
(*SVMInfoRes)(nil), // 4: manager.SVMInfoRes
|
||||
(*CVMInfoRes)(nil), // 4: manager.CVMInfoRes
|
||||
(*AttestationPolicyReq)(nil), // 5: manager.AttestationPolicyReq
|
||||
(*SVMInfoReq)(nil), // 6: manager.SVMInfoReq
|
||||
(*CVMInfoReq)(nil), // 6: manager.CVMInfoReq
|
||||
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
|
||||
}
|
||||
var file_manager_manager_proto_depIdxs = []int32{
|
||||
0, // 0: manager.ManagerService.CreateVm:input_type -> manager.CreateReq
|
||||
2, // 1: manager.ManagerService.RemoveVm:input_type -> manager.RemoveReq
|
||||
6, // 2: manager.ManagerService.SVMInfo:input_type -> manager.SVMInfoReq
|
||||
6, // 2: manager.ManagerService.CVMInfo:input_type -> manager.CVMInfoReq
|
||||
5, // 3: manager.ManagerService.AttestationPolicy:input_type -> manager.AttestationPolicyReq
|
||||
1, // 4: manager.ManagerService.CreateVm:output_type -> manager.CreateRes
|
||||
7, // 5: manager.ManagerService.RemoveVm:output_type -> google.protobuf.Empty
|
||||
4, // 6: manager.ManagerService.SVMInfo:output_type -> manager.SVMInfoRes
|
||||
4, // 6: manager.ManagerService.CVMInfo:output_type -> manager.CVMInfoRes
|
||||
3, // 7: manager.ManagerService.AttestationPolicy:output_type -> manager.AttestationPolicyRes
|
||||
4, // [4:8] is the sub-list for method output_type
|
||||
0, // [0:4] is the sub-list for method input_type
|
||||
|
||||
@@ -12,7 +12,7 @@ option go_package = "./manager";
|
||||
service ManagerService {
|
||||
rpc CreateVm(CreateReq) returns (CreateRes) {}
|
||||
rpc RemoveVm(RemoveReq) returns (google.protobuf.Empty) {}
|
||||
rpc SVMInfo(SVMInfoReq) returns (SVMInfoRes) {}
|
||||
rpc CVMInfo(CVMInfoReq) returns (CVMInfoRes) {}
|
||||
rpc AttestationPolicy(AttestationPolicyReq) returns (AttestationPolicyRes) {}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,17 @@ message CreateReq{
|
||||
bytes agent_cvm_client_key = 3;
|
||||
bytes agent_cvm_client_cert = 4;
|
||||
string agent_cvm_server_url = 5;
|
||||
string agent_cvm_ca_url = 6;
|
||||
string ttl = 7;
|
||||
}
|
||||
|
||||
message CreateRes{
|
||||
string forwarded_port = 1;
|
||||
string svm_id = 2;
|
||||
string cvm_id = 2;
|
||||
}
|
||||
|
||||
message RemoveReq{
|
||||
string svm_id = 1;
|
||||
string cvm_id = 1;
|
||||
}
|
||||
|
||||
message AttestationPolicyRes{
|
||||
@@ -38,7 +40,7 @@ message AttestationPolicyRes{
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
message SVMInfoRes{
|
||||
message CVMInfoRes{
|
||||
string id = 1;
|
||||
string ovmf_version = 2;
|
||||
int32 cpu_num = 3;
|
||||
@@ -51,7 +53,7 @@ message AttestationPolicyReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message SVMInfoReq {
|
||||
message CVMInfoReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -4,7 +4,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.29.3
|
||||
// - protoc v5.29.0
|
||||
// source: manager/manager.proto
|
||||
|
||||
package manager
|
||||
@@ -25,7 +25,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||
const (
|
||||
ManagerService_CreateVm_FullMethodName = "/manager.ManagerService/CreateVm"
|
||||
ManagerService_RemoveVm_FullMethodName = "/manager.ManagerService/RemoveVm"
|
||||
ManagerService_SVMInfo_FullMethodName = "/manager.ManagerService/SVMInfo"
|
||||
ManagerService_CVMInfo_FullMethodName = "/manager.ManagerService/CVMInfo"
|
||||
ManagerService_AttestationPolicy_FullMethodName = "/manager.ManagerService/AttestationPolicy"
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ const (
|
||||
type ManagerServiceClient interface {
|
||||
CreateVm(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*CreateRes, error)
|
||||
RemoveVm(ctx context.Context, in *RemoveReq, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
SVMInfo(ctx context.Context, in *SVMInfoReq, opts ...grpc.CallOption) (*SVMInfoRes, error)
|
||||
CVMInfo(ctx context.Context, in *CVMInfoReq, opts ...grpc.CallOption) (*CVMInfoRes, error)
|
||||
AttestationPolicy(ctx context.Context, in *AttestationPolicyReq, opts ...grpc.CallOption) (*AttestationPolicyRes, error)
|
||||
}
|
||||
|
||||
@@ -67,10 +67,10 @@ func (c *managerServiceClient) RemoveVm(ctx context.Context, in *RemoveReq, opts
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *managerServiceClient) SVMInfo(ctx context.Context, in *SVMInfoReq, opts ...grpc.CallOption) (*SVMInfoRes, error) {
|
||||
func (c *managerServiceClient) CVMInfo(ctx context.Context, in *CVMInfoReq, opts ...grpc.CallOption) (*CVMInfoRes, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SVMInfoRes)
|
||||
err := c.cc.Invoke(ctx, ManagerService_SVMInfo_FullMethodName, in, out, cOpts...)
|
||||
out := new(CVMInfoRes)
|
||||
err := c.cc.Invoke(ctx, ManagerService_CVMInfo_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -93,7 +93,7 @@ func (c *managerServiceClient) AttestationPolicy(ctx context.Context, in *Attest
|
||||
type ManagerServiceServer interface {
|
||||
CreateVm(context.Context, *CreateReq) (*CreateRes, error)
|
||||
RemoveVm(context.Context, *RemoveReq) (*emptypb.Empty, error)
|
||||
SVMInfo(context.Context, *SVMInfoReq) (*SVMInfoRes, error)
|
||||
CVMInfo(context.Context, *CVMInfoReq) (*CVMInfoRes, error)
|
||||
AttestationPolicy(context.Context, *AttestationPolicyReq) (*AttestationPolicyRes, error)
|
||||
mustEmbedUnimplementedManagerServiceServer()
|
||||
}
|
||||
@@ -111,8 +111,8 @@ func (UnimplementedManagerServiceServer) CreateVm(context.Context, *CreateReq) (
|
||||
func (UnimplementedManagerServiceServer) RemoveVm(context.Context, *RemoveReq) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveVm not implemented")
|
||||
}
|
||||
func (UnimplementedManagerServiceServer) SVMInfo(context.Context, *SVMInfoReq) (*SVMInfoRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SVMInfo not implemented")
|
||||
func (UnimplementedManagerServiceServer) CVMInfo(context.Context, *CVMInfoReq) (*CVMInfoRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CVMInfo not implemented")
|
||||
}
|
||||
func (UnimplementedManagerServiceServer) AttestationPolicy(context.Context, *AttestationPolicyReq) (*AttestationPolicyRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AttestationPolicy not implemented")
|
||||
@@ -174,20 +174,20 @@ func _ManagerService_RemoveVm_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ManagerService_SVMInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SVMInfoReq)
|
||||
func _ManagerService_CVMInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CVMInfoReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ManagerServiceServer).SVMInfo(ctx, in)
|
||||
return srv.(ManagerServiceServer).CVMInfo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ManagerService_SVMInfo_FullMethodName,
|
||||
FullMethod: ManagerService_CVMInfo_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ManagerServiceServer).SVMInfo(ctx, req.(*SVMInfoReq))
|
||||
return srv.(ManagerServiceServer).CVMInfo(ctx, req.(*CVMInfoReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -226,8 +226,8 @@ var ManagerService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _ManagerService_RemoveVm_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SVMInfo",
|
||||
Handler: _ManagerService_SVMInfo_Handler,
|
||||
MethodName: "CVMInfo",
|
||||
Handler: _ManagerService_CVMInfo_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AttestationPolicy",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -103,6 +103,80 @@ func (_c *ManagerServiceClient_AttestationPolicy_Call) RunAndReturn(run func(con
|
||||
return _c
|
||||
}
|
||||
|
||||
// CVMInfo provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *ManagerServiceClient) CVMInfo(ctx context.Context, in *manager.CVMInfoReq, opts ...grpc.CallOption) (*manager.CVMInfoRes, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CVMInfo")
|
||||
}
|
||||
|
||||
var r0 *manager.CVMInfoRes
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *manager.CVMInfoReq, ...grpc.CallOption) (*manager.CVMInfoRes, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *manager.CVMInfoReq, ...grpc.CallOption) *manager.CVMInfoRes); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*manager.CVMInfoRes)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *manager.CVMInfoReq, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ManagerServiceClient_CVMInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CVMInfo'
|
||||
type ManagerServiceClient_CVMInfo_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CVMInfo is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *manager.CVMInfoReq
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *ManagerServiceClient_Expecter) CVMInfo(ctx interface{}, in interface{}, opts ...interface{}) *ManagerServiceClient_CVMInfo_Call {
|
||||
return &ManagerServiceClient_CVMInfo_Call{Call: _e.mock.On("CVMInfo",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_CVMInfo_Call) Run(run func(ctx context.Context, in *manager.CVMInfoReq, opts ...grpc.CallOption)) *ManagerServiceClient_CVMInfo_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*manager.CVMInfoReq), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_CVMInfo_Call) Return(_a0 *manager.CVMInfoRes, _a1 error) *ManagerServiceClient_CVMInfo_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_CVMInfo_Call) RunAndReturn(run func(context.Context, *manager.CVMInfoReq, ...grpc.CallOption) (*manager.CVMInfoRes, error)) *ManagerServiceClient_CVMInfo_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreateVm provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *ManagerServiceClient) CreateVm(ctx context.Context, in *manager.CreateReq, opts ...grpc.CallOption) (*manager.CreateRes, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
@@ -251,80 +325,6 @@ func (_c *ManagerServiceClient_RemoveVm_Call) RunAndReturn(run func(context.Cont
|
||||
return _c
|
||||
}
|
||||
|
||||
// SVMInfo provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *ManagerServiceClient) SVMInfo(ctx context.Context, in *manager.SVMInfoReq, opts ...grpc.CallOption) (*manager.SVMInfoRes, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SVMInfo")
|
||||
}
|
||||
|
||||
var r0 *manager.SVMInfoRes
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *manager.SVMInfoReq, ...grpc.CallOption) (*manager.SVMInfoRes, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *manager.SVMInfoReq, ...grpc.CallOption) *manager.SVMInfoRes); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*manager.SVMInfoRes)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *manager.SVMInfoReq, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ManagerServiceClient_SVMInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SVMInfo'
|
||||
type ManagerServiceClient_SVMInfo_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SVMInfo is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *manager.SVMInfoReq
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *ManagerServiceClient_Expecter) SVMInfo(ctx interface{}, in interface{}, opts ...interface{}) *ManagerServiceClient_SVMInfo_Call {
|
||||
return &ManagerServiceClient_SVMInfo_Call{Call: _e.mock.On("SVMInfo",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_SVMInfo_Call) Run(run func(ctx context.Context, in *manager.SVMInfoReq, opts ...grpc.CallOption)) *ManagerServiceClient_SVMInfo_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*manager.SVMInfoReq), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_SVMInfo_Call) Return(_a0 *manager.SVMInfoRes, _a1 error) *ManagerServiceClient_SVMInfo_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ManagerServiceClient_SVMInfo_Call) RunAndReturn(run func(context.Context, *manager.SVMInfoReq, ...grpc.CallOption) (*manager.SVMInfoRes, error)) *ManagerServiceClient_SVMInfo_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewManagerServiceClient creates a new instance of ManagerServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManagerServiceClient(t interface {
|
||||
|
||||
+57
-12
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.53.3. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -195,12 +195,12 @@ func (_c *Service_RemoveVM_Call) RunAndReturn(run func(context.Context, string)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ReturnSVMInfo provides a mock function with given fields: ctx
|
||||
func (_m *Service) ReturnSVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
// ReturnCVMInfo provides a mock function with given fields: ctx
|
||||
func (_m *Service) ReturnCVMInfo(ctx context.Context) (string, int, string, string) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ReturnSVMInfo")
|
||||
panic("no return value specified for ReturnCVMInfo")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
@@ -237,30 +237,75 @@ func (_m *Service) ReturnSVMInfo(ctx context.Context) (string, int, string, stri
|
||||
return r0, r1, r2, r3
|
||||
}
|
||||
|
||||
// Service_ReturnSVMInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReturnSVMInfo'
|
||||
type Service_ReturnSVMInfo_Call struct {
|
||||
// Service_ReturnCVMInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReturnCVMInfo'
|
||||
type Service_ReturnCVMInfo_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ReturnSVMInfo is a helper method to define mock.On call
|
||||
// ReturnCVMInfo is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
func (_e *Service_Expecter) ReturnSVMInfo(ctx interface{}) *Service_ReturnSVMInfo_Call {
|
||||
return &Service_ReturnSVMInfo_Call{Call: _e.mock.On("ReturnSVMInfo", ctx)}
|
||||
func (_e *Service_Expecter) ReturnCVMInfo(ctx interface{}) *Service_ReturnCVMInfo_Call {
|
||||
return &Service_ReturnCVMInfo_Call{Call: _e.mock.On("ReturnCVMInfo", ctx)}
|
||||
}
|
||||
|
||||
func (_c *Service_ReturnSVMInfo_Call) Run(run func(ctx context.Context)) *Service_ReturnSVMInfo_Call {
|
||||
func (_c *Service_ReturnCVMInfo_Call) Run(run func(ctx context.Context)) *Service_ReturnCVMInfo_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ReturnSVMInfo_Call) Return(_a0 string, _a1 int, _a2 string, _a3 string) *Service_ReturnSVMInfo_Call {
|
||||
func (_c *Service_ReturnCVMInfo_Call) Return(_a0 string, _a1 int, _a2 string, _a3 string) *Service_ReturnCVMInfo_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2, _a3)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ReturnSVMInfo_Call) RunAndReturn(run func(context.Context) (string, int, string, string)) *Service_ReturnSVMInfo_Call {
|
||||
func (_c *Service_ReturnCVMInfo_Call) RunAndReturn(run func(context.Context) (string, int, string, string)) *Service_ReturnCVMInfo_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Shutdown provides a mock function with no fields
|
||||
func (_m *Service) Shutdown() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Shutdown")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Service_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown'
|
||||
type Service_Shutdown_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Shutdown is a helper method to define mock.On call
|
||||
func (_e *Service_Expecter) Shutdown() *Service_Shutdown_Call {
|
||||
return &Service_Shutdown_Call{Call: _e.mock.On("Shutdown")}
|
||||
}
|
||||
|
||||
func (_c *Service_Shutdown_Call) Run(run func()) *Service_Shutdown_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_Shutdown_Call) Return(_a0 error) *Service_Shutdown_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_Shutdown_Call) RunAndReturn(run func() error) *Service_Shutdown_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
groups:
|
||||
- name: cocos-manager-alerts
|
||||
rules:
|
||||
# Service Down Alert - Primary method using up metric
|
||||
- alert: CocosManagerDown
|
||||
expr: up{job="cocos-manager"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
service: cocos-manager
|
||||
annotations:
|
||||
summary: "Cocos Manager service is down"
|
||||
description: "Cocos Manager service has been down for more than 1 minute. Instance: {{ $labels.instance }}"
|
||||
|
||||
# Alternative: No metrics received (for cases where up metric might not be reliable)
|
||||
- alert: CocosManagerNoMetrics
|
||||
expr: absent(up{job="cocos-manager"})
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
service: cocos-manager
|
||||
annotations:
|
||||
summary: "No metrics received from Cocos Manager"
|
||||
description: "No metrics have been received from Cocos Manager for more than 2 minutes"
|
||||
|
||||
# Health endpoint specific alert (if you expose health as a metric)
|
||||
- alert: CocosManagerUnhealthy
|
||||
expr: health_check{job="cocos-manager"} == 0
|
||||
for: 30s
|
||||
labels:
|
||||
severity: warning
|
||||
service: cocos-manager
|
||||
annotations:
|
||||
summary: "Cocos Manager health check failing"
|
||||
description: "Cocos Manager health check has been failing for more than 30 seconds. Instance: {{ $labels.instance }}"
|
||||
|
||||
# High latency alert
|
||||
- alert: CocosManagerHighLatency
|
||||
expr: histogram_quantile(0.95, rate(cocos_manager_latency_bucket[5m])) > 2
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: cocos-manager
|
||||
annotations:
|
||||
summary: "Cocos Manager high latency"
|
||||
description: "Cocos Manager 95th percentile latency is above 2 seconds for more than 5 minutes. Current value: {{ $value }}s"
|
||||
|
||||
# High error rate alert
|
||||
- alert: CocosManagerHighErrorRate
|
||||
expr: rate(cocos_manager_errors_total[5m]) > 0.1
|
||||
for: 2m
|
||||
labels:
|
||||
severity: warning
|
||||
service: cocos-manager
|
||||
annotations:
|
||||
summary: "Cocos Manager high error rate"
|
||||
description: "Cocos Manager error rate is above 10% for more than 2 minutes. Current rate: {{ $value }}"
|
||||
@@ -0,0 +1,56 @@
|
||||
global:
|
||||
smtp_smarthost: 'localhost:587'
|
||||
smtp_from: 'alerts@yourcompany.com'
|
||||
|
||||
route:
|
||||
group_by: ['alertname', 'service']
|
||||
group_wait: 10s
|
||||
group_interval: 10s
|
||||
repeat_interval: 1h
|
||||
receiver: 'web.hook'
|
||||
routes:
|
||||
- match:
|
||||
severity: critical
|
||||
receiver: 'critical-alerts'
|
||||
- match:
|
||||
severity: warning
|
||||
receiver: 'warning-alerts'
|
||||
|
||||
receivers:
|
||||
- name: 'web.hook'
|
||||
webhook_configs:
|
||||
- url: 'http://127.0.0.1:5001/'
|
||||
|
||||
- name: 'critical-alerts'
|
||||
email_configs:
|
||||
- to: 'oncall@yourcompany.com'
|
||||
subject: 'CRITICAL: {{ .GroupLabels.service }} Alert'
|
||||
body: |
|
||||
{{ range .Alerts }}
|
||||
Alert: {{ .Annotations.summary }}
|
||||
Description: {{ .Annotations.description }}
|
||||
Labels: {{ range .Labels.SortedPairs }}{{ .Name }}: {{ .Value }} {{ end }}
|
||||
{{ end }}
|
||||
slack_configs:
|
||||
- api_url: 'YOUR_SLACK_WEBHOOK_URL'
|
||||
channel: '#alerts'
|
||||
title: 'CRITICAL Alert: {{ .GroupLabels.service }}'
|
||||
text: '{{ range .Alerts }}{{ .Annotations.summary }}: {{ .Annotations.description }}{{ end }}'
|
||||
|
||||
- name: 'warning-alerts'
|
||||
email_configs:
|
||||
- to: 'team@yourcompany.com'
|
||||
subject: 'WARNING: {{ .GroupLabels.service }} Alert'
|
||||
body: |
|
||||
{{ range .Alerts }}
|
||||
Alert: {{ .Annotations.summary }}
|
||||
Description: {{ .Annotations.description }}
|
||||
Labels: {{ range .Labels.SortedPairs }}{{ .Name }}: {{ .Value }} {{ end }}
|
||||
{{ end }}
|
||||
|
||||
inhibit_rules:
|
||||
- source_match:
|
||||
severity: 'critical'
|
||||
target_match:
|
||||
severity: 'warning'
|
||||
equal: ['alertname', 'service']
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user