diff --git a/benchmarking/locust/common/ateapi_pb2.py b/benchmarking/locust/common/ateapi_pb2.py index eeed4b8ff9..76eeaf6d2f 100644 --- a/benchmarking/locust/common/ateapi_pb2.py +++ b/benchmarking/locust/common/ateapi_pb2.py @@ -40,7 +40,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x61teapi.proto\x12\x06\x61teapi\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x86\x01\n\x11LocalSnapshotInfo\x12\x15\n\rsnapshot_name\x18\x01 \x01(\t\x12%\n\x1dnode_vms_with_local_snapshots\x18\x02 \x03(\t\x12\x33\n\rcontent_scope\x18\x03 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\"w\n\x08Selector\x12\x37\n\x0cmatch_labels\x18\x01 \x03(\x0b\x32!.ateapi.Selector.MatchLabelsEntry\x1a\x32\n\x10MatchLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xb2\x01\n\x10ResourceMetadata\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0b\n\x03uid\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\x03\x12/\n\x0b\x63reate_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0bupdate_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xdc\x02\n\x0e\x45xternalVolume\x12\x13\n\x0bvolume_name\x18\x01 \x01(\t\x12\x19\n\x11storage_volume_id\x18\x02 \x01(\t\x12\x13\n\x0bvolume_type\x18\x03 \x01(\t\x12-\n\x06status\x18\x04 \x01(\x0e\x32\x1d.ateapi.ExternalVolume.Status\x12\x41\n\x0evolume_context\x18\x05 \x03(\x0b\x32).ateapi.ExternalVolume.VolumeContextEntry\x1a\x34\n\x12VolumeContextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"]\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x12\n\x0eSTATUS_CREATED\x10\x02\x12\x13\n\x0fSTATUS_DELETING\x10\x03\"\xde\x01\n\x05\x41\x63tor\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12)\n\x0e\x61\x63tor_template\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12)\n\x0fworker_selector\x18\x05 \x01(\x0b\x32\x10.ateapi.Selector\x12.\n\x13source_snapshot_tag\x18\x06 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12#\n\x06status\x18\x07 \x01(\x0b\x32\x13.ateapi.ActorStatus\"]\n\x0c\x45gressPolicy\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12!\n\x05rules\x18\x02 \x03(\x0b\x32\x12.ateapi.EgressRule\"\x82\x01\n\nEgressRule\x12\'\n\thostnames\x18\x01 \x01(\x0b\x32\x14.ateapi.HostnameRule\x12&\n\tip_blocks\x18\x02 \x01(\x0b\x32\x13.ateapi.IPBlockRule\x12#\n\x03\x61ll\x18\x03 \x01(\x0b\x32\x16.google.protobuf.Empty\"L\n\x0cHostnameRule\x12\x10\n\x08patterns\x18\x01 \x03(\t\x12*\n\x07\x65\x66\x66\x65\x63ts\x18\x02 \x01(\x0b\x32\x19.ateapi.EgressRuleEffects\"\x1c\n\x0bIPBlockRule\x12\r\n\x05\x63idrs\x18\x01 \x03(\t\"U\n\x11\x45gressRuleEffects\x12@\n\x15inject_static_headers\x18\x01 \x03(\x0b\x32!.ateapi.CredentialHeaderInjection\"S\n\x19\x43redentialHeaderInjection\x12\x0e\n\x06header\x18\x01 \x01(\t\x12\x0e\n\x06prefix\x18\x02 \x01(\t\x12\x16\n\x0e\x63redential_uri\x18\x03 \x01(\t\"\xb3\x03\n\x0b\x41\x63torStatus\x12!\n\x05state\x18\x01 \x01(\x0e\x32\x12.ateapi.ActorState\x12\x33\n\x11worker_assignment\x18\x02 \x01(\x0b\x32\x18.ateapi.WorkerAssignment\x12!\n\x19in_progress_snapshot_name\x18\x03 \x01(\t\x12*\n\x0flatest_snapshot\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x36\n\x13local_snapshot_info\x18\x05 \x01(\x0b\x32\x19.ateapi.LocalSnapshotInfo\x12\x31\n)in_progress_snapshot_source_actor_version\x18\x06 \x01(\x03\x12-\n\ractor_volumes\x18\x07 \x03(\x0b\x32\x16.ateapi.ExternalVolume\x12\'\n\x1fin_progress_local_snapshot_name\x18\x08 \x01(\t\x12:\n\x0fsource_snapshot\x18\t \x01(\x0b\x32!.ateapi.ActorSourceSnapshotStatus\"V\n\x19\x41\x63torSourceSnapshotStatus\x12#\n\x08snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x14\n\x0csnapshot_uid\x18\x02 \x01(\t\"\xa7\x01\n\x10WorkerAssignment\x12!\n\x06worker\x18\x06 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x18\n\x10worker_namespace\x18\x01 \x01(\t\x12\x13\n\x0bworker_pool\x18\x02 \x01(\t\x12\x12\n\nworker_pod\x18\x03 \x01(\t\x12\x16\n\x0eworker_pod_uid\x18\x04 \x01(\t\x12\x15\n\rworker_pod_ip\x18\x05 \x01(\t\"h\n\rActorSnapshot\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12+\n\x06status\x18\x02 \x01(\x0b\x32\x1b.ateapi.ActorSnapshotStatus\"\x88\x02\n\x13\x41\x63torSnapshotStatus\x12\'\n\x0csource_actor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x18\n\x10source_actor_uid\x18\x02 \x01(\t\x12\x1c\n\x14source_actor_version\x18\x03 \x01(\x03\x12\x1a\n\x12\x61\x63tor_template_uid\x18\x06 \x01(\t\x12\x33\n\rcontent_scope\x18\x07 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12\x14\n\x0csnapshot_uri\x18\x08 \x01(\t\x12)\n\x0e\x61\x63tor_template\x18\t \x01(\x0b\x32\x11.ateapi.ObjectRef\"\x91\x01\n\x10\x41\x63torSnapshotTag\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12#\n\x08snapshot\x18\x02 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12,\n\x05scope\x18\x03 \x01(\x0e\x32\x1d.ateapi.ActorSnapshotTagScope\"6\n\x08\x41tespace\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\"+\n\tObjectRef\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xe3\x02\n\rActorTemplate\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12)\n\x0fworker_selector\x18\x02 \x01(\x0b\x32\x10.ateapi.Selector\x12%\n\ncontainers\x18\x03 \x03(\x0b\x32\x11.ateapi.Container\x12\x1f\n\x07volumes\x18\x04 \x03(\x0b\x32\x0e.ateapi.Volume\x12\x31\n\x10snapshots_config\x18\x05 \x01(\x0b\x32\x17.ateapi.SnapshotsConfig\x12-\n\x0esandbox_config\x18\x06 \x01(\x0b\x32\x15.ateapi.SandboxConfig\x12$\n\tresources\x18\x07 \x01(\x0b\x32\x11.ateapi.Resources\x12+\n\x06status\x18\x08 \x01(\x0b\x32\x1b.ateapi.ActorTemplateStatus\"+\n\tResources\x12\x1e\n\x06limits\x18\x01 \x03(\x0b\x32\x0e.ateapi.Limits\"(\n\x06Limits\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\t\"\x96\x01\n\x14GoldenSnapshotStatus\x12*\n\x0fgolden_snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12;\n\x17take_golden_snapshot_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x15\n\rerror_message\x18\x03 \x01(\t\"S\n\x13\x41\x63torTemplateStatus\x12<\n\x16golden_snapshot_status\x18\x01 \x01(\x0b\x32\x1c.ateapi.GoldenSnapshotStatus\"Q\n\rSandboxConfig\x12+\n\rsandbox_class\x18\x01 \x01(\x0e\x32\x14.ateapi.SandboxClass\x12\x13\n\x0b\x63onfig_name\x18\x02 \x01(\t\"\xb7\x01\n\x0fSnapshotsConfig\x12.\n\x08on_pause\x18\x01 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12/\n\ton_commit\x18\x02 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12)\n\ton_resume\x18\x03 \x01(\x0b\x32\x16.ateapi.OnResumeConfig\x12\x18\n\x10storage_location\x18\x04 \x01(\t\"9\n\x0eOnResumeConfig\x12\'\n\tfrom_data\x18\x01 \x01(\x0e\x32\x14.ateapi.ResumeSource\"\x92\x02\n\tContainer\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05image\x18\x02 \x01(\t\x12\x0f\n\x07\x63ommand\x18\x03 \x03(\t\x12\x0c\n\x04\x61rgs\x18\x04 \x03(\t\x12\x1b\n\x03\x65nv\x18\x05 \x03(\x0b\x32\x0e.ateapi.EnvVar\x12\'\n\x06readyz\x18\x06 \x01(\x0b\x32\x17.ateapi.ContainerReadyz\x12*\n\rvolume_mounts\x18\x07 \x03(\x0b\x32\x13.ateapi.VolumeMount\x12\x31\n\x10security_context\x18\x08 \x01(\x0b\x32\x17.ateapi.SecurityContext\x12$\n\tresources\x18\t \x01(\x0b\x32\x11.ateapi.Resources\"=\n\x0fSecurityContext\x12*\n\x0c\x63\x61pabilities\x18\x01 \x01(\x0b\x32\x14.ateapi.Capabilities\")\n\x0c\x43\x61pabilities\x12\x0b\n\x03\x61\x64\x64\x18\x01 \x03(\t\x12\x0c\n\x04\x64rop\x18\x02 \x03(\t\"%\n\x06\x45nvVar\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"S\n\x0f\x43ontainerReadyz\x12\'\n\x08http_get\x18\x01 \x01(\x0b\x32\x15.ateapi.HTTPGetAction\x12\x17\n\x0ftimeout_seconds\x18\x02 \x01(\x05\"+\n\rHTTPGetAction\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\"\xfa\x01\n\x06Volume\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x33\n\x0b\x64urable_dir\x18\x02 \x01(\x0b\x32\x1e.ateapi.DurableDirVolumeSource\x12@\n\x18\x65xternal_volume_template\x18\x03 \x01(\x0b\x32\x1e.ateapi.ExternalVolumeTemplate\x12\x33\n\x0bsystem_info\x18\x05 \x01(\x0b\x32\x1e.ateapi.SystemInfoVolumeSource\x12(\n\x05image\x18\x06 \x01(\x0b\x32\x19.ateapi.ImageVolumeSource\x12\x0c\n\x04type\x18\x04 \x01(\t\"&\n\x11ImageVolumeSource\x12\x11\n\treference\x18\x01 \x01(\t\"\x18\n\x16\x44urableDirVolumeSource\"F\n\x16\x45xternalVolumeTemplate\x12\x10\n\x08\x63\x61pacity\x18\x01 \x01(\t\x12\x1a\n\x12storage_class_name\x18\x02 \x01(\t\"L\n\x16SystemInfoVolumeSource\x12\x32\n\x0c\x64\x61ta_sources\x18\x01 \x03(\x0b\x32\x1c.ateapi.SystemInfoDataSource\"\x84\x01\n\x14SystemInfoDataSource\x12\x37\n\x0e\x61\x63tor_metadata\x18\x01 \x01(\x0b\x32\x1f.ateapi.ActorMetadataDataSource\x12\x33\n\x0ctrust_bundle\x18\x02 \x01(\x0b\x32\x1d.ateapi.TrustBundleDataSource\"C\n\x17\x41\x63torMetadataDataSource\x12(\n\x05items\x18\x01 \x03(\x0b\x32\x19.ateapi.ActorMetadataItem\"L\n\x11\x41\x63torMetadataItem\x12)\n\x05\x66ield\x18\x01 \x01(\x0e\x32\x1a.ateapi.ActorMetadataField\x12\x0c\n\x04path\x18\x02 \x01(\t\"3\n\x15TrustBundleDataSource\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\"/\n\x0bVolumeMount\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nmount_path\x18\x02 \x01(\t\";\n\x15\x43reateAtespaceRequest\x12\"\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x10.ateapi.Atespace\"9\n\x12GetAtespaceRequest\x12#\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"=\n\x14ListAtespacesRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x12\n\npage_token\x18\x02 \x01(\t\"U\n\x15ListAtespacesResponse\x12#\n\tatespaces\x18\x01 \x03(\x0b\x32\x10.ateapi.Atespace\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"<\n\x15\x44\x65leteAtespaceRequest\x12#\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"K\n\x1a\x43reateActorTemplateRequest\x12-\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x15.ateapi.ActorTemplate\"D\n\x17GetActorTemplateRequest\x12)\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"T\n\x19ListActorTemplatesRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"e\n\x1aListActorTemplatesResponse\x12.\n\x0f\x61\x63tor_templates\x18\x01 \x03(\x0b\x32\x15.ateapi.ActorTemplate\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"G\n\x1a\x44\x65leteActorTemplateRequest\x12)\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"3\n\x0fGetActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"2\n\x12\x43reateActorRequest\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"2\n\x12UpdateActorRequest\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"7\n\x13SuspendActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"4\n\x14SuspendActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"5\n\x11PauseActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"2\n\x12PauseActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"D\n\x12ResumeActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x0c\n\x04\x62oot\x18\x02 \x01(\x08\"D\n\x13ResumeActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\x12\x0f\n\x07resumed\x18\x02 \x01(\x08\"I\n\x12\x44\x65leteActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tany_state\x18\x02 \x01(\x08\"?\n\x1bGetActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"o\n\x1e\x43reateActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12+\n\regress_policy\x18\x02 \x01(\x0b\x32\x14.ateapi.EgressPolicy\"o\n\x1eUpdateActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12+\n\regress_policy\x18\x02 \x01(\x0b\x32\x14.ateapi.EgressPolicy\"B\n\x1e\x44\x65leteActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"D\n\x17GetActorSnapshotRequest\x12)\n\x0e\x61\x63tor_snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"K\n\x1aGetActorSnapshotTagRequest\x12-\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"T\n\x19ListActorSnapshotsRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"e\n\x1aListActorSnapshotsResponse\x12.\n\x0f\x61\x63tor_snapshots\x18\x01 \x03(\x0b\x32\x15.ateapi.ActorSnapshot\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"U\n\x1d\x43reateActorSnapshotTagRequest\x12\x34\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x18.ateapi.ActorSnapshotTag\"U\n\x1dUpdateActorSnapshotTagRequest\x12\x34\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x18.ateapi.ActorSnapshotTag\"N\n\x1d\x44\x65leteActorSnapshotTagRequest\x12-\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"-\n\rDeleteOptions\x12\x0f\n\x07version\x18\x01 \x01(\x03\x12\x0b\n\x03uid\x18\x02 \x01(\t\";\n\x12ListWorkersRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x12\n\npage_token\x18\x02 \x01(\t\"O\n\x13ListWorkersResponse\x12\x1f\n\x07workers\x18\x01 \x03(\x0b\x32\x0e.ateapi.Worker\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"5\n\x10GetWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"5\n\x13\x43reateWorkerRequest\x12\x1e\n\x06worker\x18\x01 \x01(\x0b\x32\x0e.ateapi.Worker\"5\n\x13UpdateWorkerRequest\x12\x1e\n\x06worker\x18\x01 \x01(\x0b\x32\x0e.ateapi.Worker\"`\n\x13\x44\x65leteWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12&\n\x07options\x18\x02 \x01(\x0b\x32\x15.ateapi.DeleteOptions\"7\n\x12\x44rainWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"L\n\x11ListActorsRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"L\n\x12ListActorsResponse\x12\x1d\n\x06\x61\x63tors\x18\x01 \x03(\x0b\x32\r.ateapi.Actor\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\xf0\x02\n\x06Worker\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12\x18\n\x10worker_namespace\x18\x02 \x01(\t\x12\x13\n\x0bworker_pool\x18\x03 \x01(\t\x12\x12\n\nworker_pod\x18\x04 \x01(\t\x12\x16\n\x0eworker_pod_uid\x18\x05 \x01(\t\x12\x11\n\tnode_name\x18\x06 \x01(\t\x12\n\n\x02ip\x18\x07 \x01(\t\x12\x15\n\rsandbox_class\x18\x08 \x01(\t\x12*\n\x06labels\x18\t \x03(\x0b\x32\x1a.ateapi.Worker.LabelsEntry\x12(\n\x08\x63\x61pacity\x18\n \x01(\x0b\x32\x16.ateapi.WorkerCapacity\x12$\n\x06status\x18\x0b \x01(\x0b\x32\x14.ateapi.WorkerStatus\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\x0cWorkerStatus\x12\"\n\x05state\x18\x01 \x01(\x0e\x32\x13.ateapi.WorkerState\x12+\n\nassignment\x18\x02 \x01(\x0b\x32\x17.ateapi.ActorAssignment\"9\n\x0eWorkerCapacity\x12\x11\n\tcpu_milli\x18\x01 \x01(\x03\x12\x14\n\x0cmemory_bytes\x18\x02 \x01(\x03\"u\n\x0f\x41\x63torAssignment\x12 \n\x05\x61\x63tor\x18\x02 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tactor_uid\x18\x03 \x01(\t\x12-\n\x12\x61\x63tor_template_ref\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef\"[\n\x0eMintJWTRequest\x12\x10\n\x08\x61udience\x18\x01 \x03(\t\x12\x10\n\x08\x61tespace\x18\x02 \x01(\t\x12\x12\n\nactor_name\x18\x03 \x01(\t\x12\x11\n\tactor_uid\x18\x04 \x01(\t\"$\n\x0fMintJWTResponse\x12\x11\n\tactor_jwt\x18\x01 \x01(\t\"\xa7\x01\n\x0fMintCertRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12#\n\x1b\x63\x65rtificate_signing_request\x18\x02 \x01(\x0c\x12\x1a\n\x12\x65xpected_actor_uid\x18\x03 \x01(\t\x12\x30\n\x07purpose\x18\x04 \x01(\x0e\x32\x1f.ateapi.ActorCertificatePurpose\".\n\x10MintCertResponse\x12\x1a\n\x12\x61\x63tor_certificates\x18\x01 \x03(\x0c*\x80\x01\n\x14SnapshotContentScope\x12&\n\"SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED\x10\x00\x12\x1f\n\x1bSNAPSHOT_CONTENT_SCOPE_FULL\x10\x01\x12\x1f\n\x1bSNAPSHOT_CONTENT_SCOPE_DATA\x10\x02*\x90\x01\n\x15\x41\x63torSnapshotTagScope\x12(\n$ACTOR_SNAPSHOT_TAG_SCOPE_UNSPECIFIED\x10\x00\x12%\n!ACTOR_SNAPSHOT_TAG_SCOPE_ATESPACE\x10\x01\x12&\n\"ACTOR_SNAPSHOT_TAG_SCOPE_PUBLISHED\x10\x02*\xf7\x01\n\nActorState\x12\x1b\n\x17\x41\x43TOR_STATE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x41\x43TOR_STATE_RESUMING\x10\x01\x12\x17\n\x13\x41\x43TOR_STATE_RUNNING\x10\x02\x12\x1a\n\x16\x41\x43TOR_STATE_SUSPENDING\x10\x03\x12\x19\n\x15\x41\x43TOR_STATE_SUSPENDED\x10\x04\x12\x17\n\x13\x41\x43TOR_STATE_PAUSING\x10\x05\x12\x16\n\x12\x41\x43TOR_STATE_PAUSED\x10\x06\x12\x17\n\x13\x41\x43TOR_STATE_CRASHED\x10\x07\x12\x18\n\x14\x41\x43TOR_STATE_DELETING\x10\x08*b\n\x0cSandboxClass\x12\x1d\n\x19SANDBOX_CLASS_UNSPECIFIED\x10\x00\x12\x18\n\x14SANDBOX_CLASS_GVISOR\x10\x01\x12\x19\n\x15SANDBOX_CLASS_MICROVM\x10\x02*d\n\x0cResumeSource\x12\x1d\n\x19RESUME_SOURCE_UNSPECIFIED\x10\x00\x12\x1b\n\x17RESUME_SOURCE_COLD_BOOT\x10\x01\x12\x18\n\x14RESUME_SOURCE_GOLDEN\x10\x02*\x9a\x01\n\x12\x41\x63torMetadataField\x12$\n ACTOR_METADATA_FIELD_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x41\x43TOR_METADATA_FIELD_NAME\x10\x01\x12!\n\x1d\x41\x43TOR_METADATA_FIELD_ATESPACE\x10\x02\x12\x1c\n\x18\x41\x43TOR_METADATA_FIELD_UID\x10\x03*_\n\x0bWorkerState\x12\x1c\n\x18WORKER_STATE_UNSPECIFIED\x10\x00\x12\x17\n\x13WORKER_STATE_ACTIVE\x10\x01\x12\x19\n\x15WORKER_STATE_DRAINING\x10\x02*k\n\x17\x41\x63torCertificatePurpose\x12)\n%ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED\x10\x00\x12%\n!ACTOR_CERTIFICATE_PURPOSE_ATUNNEL\x10\x01\x32\x84\x13\n\x07\x43ontrol\x12\x34\n\x08GetActor\x12\x17.ateapi.GetActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n\x0b\x43reateActor\x12\x1a.ateapi.CreateActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n\x0bUpdateActor\x12\x1a.ateapi.UpdateActorRequest\x1a\r.ateapi.Actor\"\x00\x12K\n\x0cSuspendActor\x12\x1b.ateapi.SuspendActorRequest\x1a\x1c.ateapi.SuspendActorResponse\"\x00\x12\x45\n\nPauseActor\x12\x19.ateapi.PauseActorRequest\x1a\x1a.ateapi.PauseActorResponse\"\x00\x12H\n\x0bResumeActor\x12\x1a.ateapi.ResumeActorRequest\x1a\x1b.ateapi.ResumeActorResponse\"\x00\x12:\n\x0b\x44\x65leteActor\x12\x1a.ateapi.DeleteActorRequest\x1a\r.ateapi.Actor\"\x00\x12S\n\x14GetActorEgressPolicy\x12#.ateapi.GetActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17\x43reateActorEgressPolicy\x12&.ateapi.CreateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17UpdateActorEgressPolicy\x12&.ateapi.UpdateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17\x44\x65leteActorEgressPolicy\x12&.ateapi.DeleteActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12L\n\x10GetActorSnapshot\x12\x1f.ateapi.GetActorSnapshotRequest\x1a\x15.ateapi.ActorSnapshot\"\x00\x12U\n\x13GetActorSnapshotTag\x12\".ateapi.GetActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12]\n\x12ListActorSnapshots\x12!.ateapi.ListActorSnapshotsRequest\x1a\".ateapi.ListActorSnapshotsResponse\"\x00\x12[\n\x16\x43reateActorSnapshotTag\x12%.ateapi.CreateActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12[\n\x16UpdateActorSnapshotTag\x12%.ateapi.UpdateActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12[\n\x16\x44\x65leteActorSnapshotTag\x12%.ateapi.DeleteActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12H\n\x0bListWorkers\x12\x1a.ateapi.ListWorkersRequest\x1a\x1b.ateapi.ListWorkersResponse\"\x00\x12\x37\n\tGetWorker\x12\x18.ateapi.GetWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0c\x43reateWorker\x12\x1b.ateapi.CreateWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0cUpdateWorker\x12\x1b.ateapi.UpdateWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0c\x44\x65leteWorker\x12\x1b.ateapi.DeleteWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12;\n\x0b\x44rainWorker\x12\x1a.ateapi.DrainWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12\x45\n\nListActors\x12\x19.ateapi.ListActorsRequest\x1a\x1a.ateapi.ListActorsResponse\"\x00\x12\x43\n\x0e\x43reateAtespace\x12\x1d.ateapi.CreateAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12=\n\x0bGetAtespace\x12\x1a.ateapi.GetAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12N\n\rListAtespaces\x12\x1c.ateapi.ListAtespacesRequest\x1a\x1d.ateapi.ListAtespacesResponse\"\x00\x12\x43\n\x0e\x44\x65leteAtespace\x12\x1d.ateapi.DeleteAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12R\n\x13\x43reateActorTemplate\x12\".ateapi.CreateActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12L\n\x10GetActorTemplate\x12\x1f.ateapi.GetActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12]\n\x12ListActorTemplates\x12!.ateapi.ListActorTemplatesRequest\x1a\".ateapi.ListActorTemplatesResponse\"\x00\x12R\n\x13\x44\x65leteActorTemplate\x12\".ateapi.DeleteActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x32\x8a\x01\n\rActorIdentity\x12:\n\x07MintJWT\x12\x16.ateapi.MintJWTRequest\x1a\x17.ateapi.MintJWTResponse\x12=\n\x08MintCert\x12\x17.ateapi.MintCertRequest\x1a\x18.ateapi.MintCertResponseB9Z7github.com/agent-substrate/substrate/pkg/proto/ateapipbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x61teapi.proto\x12\x06\x61teapi\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x86\x01\n\x11LocalSnapshotInfo\x12\x15\n\rsnapshot_name\x18\x01 \x01(\t\x12%\n\x1dnode_vms_with_local_snapshots\x18\x02 \x03(\t\x12\x33\n\rcontent_scope\x18\x03 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\"w\n\x08Selector\x12\x37\n\x0cmatch_labels\x18\x01 \x03(\x0b\x32!.ateapi.Selector.MatchLabelsEntry\x1a\x32\n\x10MatchLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xb2\x01\n\x10ResourceMetadata\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0b\n\x03uid\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\x03\x12/\n\x0b\x63reate_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0bupdate_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xdc\x02\n\x0e\x45xternalVolume\x12\x13\n\x0bvolume_name\x18\x01 \x01(\t\x12\x19\n\x11storage_volume_id\x18\x02 \x01(\t\x12\x13\n\x0bvolume_type\x18\x03 \x01(\t\x12-\n\x06status\x18\x04 \x01(\x0e\x32\x1d.ateapi.ExternalVolume.Status\x12\x41\n\x0evolume_context\x18\x05 \x03(\x0b\x32).ateapi.ExternalVolume.VolumeContextEntry\x1a\x34\n\x12VolumeContextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"]\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x12\n\x0eSTATUS_CREATED\x10\x02\x12\x13\n\x0fSTATUS_DELETING\x10\x03\"\xde\x01\n\x05\x41\x63tor\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12)\n\x0e\x61\x63tor_template\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12)\n\x0fworker_selector\x18\x05 \x01(\x0b\x32\x10.ateapi.Selector\x12.\n\x13source_snapshot_tag\x18\x06 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12#\n\x06status\x18\x07 \x01(\x0b\x32\x13.ateapi.ActorStatus\"]\n\x0c\x45gressPolicy\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12!\n\x05rules\x18\x02 \x03(\x0b\x32\x12.ateapi.EgressRule\"\x82\x01\n\nEgressRule\x12\'\n\thostnames\x18\x01 \x01(\x0b\x32\x14.ateapi.HostnameRule\x12&\n\tip_blocks\x18\x02 \x01(\x0b\x32\x13.ateapi.IPBlockRule\x12#\n\x03\x61ll\x18\x03 \x01(\x0b\x32\x16.google.protobuf.Empty\"L\n\x0cHostnameRule\x12\x10\n\x08patterns\x18\x01 \x03(\t\x12*\n\x07\x65\x66\x66\x65\x63ts\x18\x02 \x01(\x0b\x32\x19.ateapi.EgressRuleEffects\"\x1c\n\x0bIPBlockRule\x12\r\n\x05\x63idrs\x18\x01 \x03(\t\"U\n\x11\x45gressRuleEffects\x12@\n\x15inject_static_headers\x18\x01 \x03(\x0b\x32!.ateapi.CredentialHeaderInjection\"S\n\x19\x43redentialHeaderInjection\x12\x0e\n\x06header\x18\x01 \x01(\t\x12\x0e\n\x06prefix\x18\x02 \x01(\t\x12\x16\n\x0e\x63redential_uri\x18\x03 \x01(\t\"\xb3\x03\n\x0b\x41\x63torStatus\x12!\n\x05state\x18\x01 \x01(\x0e\x32\x12.ateapi.ActorState\x12\x33\n\x11worker_assignment\x18\x02 \x01(\x0b\x32\x18.ateapi.WorkerAssignment\x12!\n\x19in_progress_snapshot_name\x18\x03 \x01(\t\x12*\n\x0flatest_snapshot\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x36\n\x13local_snapshot_info\x18\x05 \x01(\x0b\x32\x19.ateapi.LocalSnapshotInfo\x12\x31\n)in_progress_snapshot_source_actor_version\x18\x06 \x01(\x03\x12-\n\ractor_volumes\x18\x07 \x03(\x0b\x32\x16.ateapi.ExternalVolume\x12\'\n\x1fin_progress_local_snapshot_name\x18\x08 \x01(\t\x12:\n\x0fsource_snapshot\x18\t \x01(\x0b\x32!.ateapi.ActorSourceSnapshotStatus\"V\n\x19\x41\x63torSourceSnapshotStatus\x12#\n\x08snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x14\n\x0csnapshot_uid\x18\x02 \x01(\t\"\xa7\x01\n\x10WorkerAssignment\x12!\n\x06worker\x18\x06 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x18\n\x10worker_namespace\x18\x01 \x01(\t\x12\x13\n\x0bworker_pool\x18\x02 \x01(\t\x12\x12\n\nworker_pod\x18\x03 \x01(\t\x12\x16\n\x0eworker_pod_uid\x18\x04 \x01(\t\x12\x15\n\rworker_pod_ip\x18\x05 \x01(\t\"h\n\rActorSnapshot\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12+\n\x06status\x18\x02 \x01(\x0b\x32\x1b.ateapi.ActorSnapshotStatus\"\x88\x02\n\x13\x41\x63torSnapshotStatus\x12\'\n\x0csource_actor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x18\n\x10source_actor_uid\x18\x02 \x01(\t\x12\x1c\n\x14source_actor_version\x18\x03 \x01(\x03\x12\x1a\n\x12\x61\x63tor_template_uid\x18\x06 \x01(\t\x12\x33\n\rcontent_scope\x18\x07 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12\x14\n\x0csnapshot_uri\x18\x08 \x01(\t\x12)\n\x0e\x61\x63tor_template\x18\t \x01(\x0b\x32\x11.ateapi.ObjectRef\"\x91\x01\n\x10\x41\x63torSnapshotTag\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12#\n\x08snapshot\x18\x02 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12,\n\x05scope\x18\x03 \x01(\x0e\x32\x1d.ateapi.ActorSnapshotTagScope\"6\n\x08\x41tespace\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\"+\n\tObjectRef\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xe3\x02\n\rActorTemplate\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12)\n\x0fworker_selector\x18\x02 \x01(\x0b\x32\x10.ateapi.Selector\x12%\n\ncontainers\x18\x03 \x03(\x0b\x32\x11.ateapi.Container\x12\x1f\n\x07volumes\x18\x04 \x03(\x0b\x32\x0e.ateapi.Volume\x12\x31\n\x10snapshots_config\x18\x05 \x01(\x0b\x32\x17.ateapi.SnapshotsConfig\x12-\n\x0esandbox_config\x18\x06 \x01(\x0b\x32\x15.ateapi.SandboxConfig\x12$\n\tresources\x18\x07 \x01(\x0b\x32\x11.ateapi.Resources\x12+\n\x06status\x18\x08 \x01(\x0b\x32\x1b.ateapi.ActorTemplateStatus\"+\n\tResources\x12\x1e\n\x06limits\x18\x01 \x03(\x0b\x32\x0e.ateapi.Limits\"(\n\x06Limits\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\t\"\x96\x01\n\x14GoldenSnapshotStatus\x12*\n\x0fgolden_snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12;\n\x17take_golden_snapshot_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x15\n\rerror_message\x18\x03 \x01(\t\"S\n\x13\x41\x63torTemplateStatus\x12<\n\x16golden_snapshot_status\x18\x01 \x01(\x0b\x32\x1c.ateapi.GoldenSnapshotStatus\"Q\n\rSandboxConfig\x12+\n\rsandbox_class\x18\x01 \x01(\x0e\x32\x14.ateapi.SandboxClass\x12\x13\n\x0b\x63onfig_name\x18\x02 \x01(\t\"\xb7\x01\n\x0fSnapshotsConfig\x12.\n\x08on_pause\x18\x01 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12/\n\ton_commit\x18\x02 \x01(\x0e\x32\x1c.ateapi.SnapshotContentScope\x12)\n\ton_resume\x18\x03 \x01(\x0b\x32\x16.ateapi.OnResumeConfig\x12\x18\n\x10storage_location\x18\x04 \x01(\t\"9\n\x0eOnResumeConfig\x12\'\n\tfrom_data\x18\x01 \x01(\x0e\x32\x14.ateapi.ResumeSource\"\x92\x02\n\tContainer\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05image\x18\x02 \x01(\t\x12\x0f\n\x07\x63ommand\x18\x03 \x03(\t\x12\x0c\n\x04\x61rgs\x18\x04 \x03(\t\x12\x1b\n\x03\x65nv\x18\x05 \x03(\x0b\x32\x0e.ateapi.EnvVar\x12\'\n\x06readyz\x18\x06 \x01(\x0b\x32\x17.ateapi.ContainerReadyz\x12*\n\rvolume_mounts\x18\x07 \x03(\x0b\x32\x13.ateapi.VolumeMount\x12\x31\n\x10security_context\x18\x08 \x01(\x0b\x32\x17.ateapi.SecurityContext\x12$\n\tresources\x18\t \x01(\x0b\x32\x11.ateapi.Resources\"=\n\x0fSecurityContext\x12*\n\x0c\x63\x61pabilities\x18\x01 \x01(\x0b\x32\x14.ateapi.Capabilities\")\n\x0c\x43\x61pabilities\x12\x0b\n\x03\x61\x64\x64\x18\x01 \x03(\t\x12\x0c\n\x04\x64rop\x18\x02 \x03(\t\"%\n\x06\x45nvVar\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"S\n\x0f\x43ontainerReadyz\x12\'\n\x08http_get\x18\x01 \x01(\x0b\x32\x15.ateapi.HTTPGetAction\x12\x17\n\x0ftimeout_seconds\x18\x02 \x01(\x05\"+\n\rHTTPGetAction\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\"\xfa\x01\n\x06Volume\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x33\n\x0b\x64urable_dir\x18\x02 \x01(\x0b\x32\x1e.ateapi.DurableDirVolumeSource\x12@\n\x18\x65xternal_volume_template\x18\x03 \x01(\x0b\x32\x1e.ateapi.ExternalVolumeTemplate\x12\x33\n\x0bsystem_info\x18\x05 \x01(\x0b\x32\x1e.ateapi.SystemInfoVolumeSource\x12(\n\x05image\x18\x06 \x01(\x0b\x32\x19.ateapi.ImageVolumeSource\x12\x0c\n\x04type\x18\x04 \x01(\t\"&\n\x11ImageVolumeSource\x12\x11\n\treference\x18\x01 \x01(\t\"\x18\n\x16\x44urableDirVolumeSource\"F\n\x16\x45xternalVolumeTemplate\x12\x10\n\x08\x63\x61pacity\x18\x01 \x01(\t\x12\x1a\n\x12storage_class_name\x18\x02 \x01(\t\"L\n\x16SystemInfoVolumeSource\x12\x32\n\x0c\x64\x61ta_sources\x18\x01 \x03(\x0b\x32\x1c.ateapi.SystemInfoDataSource\"\x84\x01\n\x14SystemInfoDataSource\x12\x37\n\x0e\x61\x63tor_metadata\x18\x01 \x01(\x0b\x32\x1f.ateapi.ActorMetadataDataSource\x12\x33\n\x0ctrust_bundle\x18\x02 \x01(\x0b\x32\x1d.ateapi.TrustBundleDataSource\"C\n\x17\x41\x63torMetadataDataSource\x12(\n\x05items\x18\x01 \x03(\x0b\x32\x19.ateapi.ActorMetadataItem\"L\n\x11\x41\x63torMetadataItem\x12)\n\x05\x66ield\x18\x01 \x01(\x0e\x32\x1a.ateapi.ActorMetadataField\x12\x0c\n\x04path\x18\x02 \x01(\t\"3\n\x15TrustBundleDataSource\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\"/\n\x0bVolumeMount\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nmount_path\x18\x02 \x01(\t\";\n\x15\x43reateAtespaceRequest\x12\"\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x10.ateapi.Atespace\"9\n\x12GetAtespaceRequest\x12#\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"=\n\x14ListAtespacesRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x12\n\npage_token\x18\x02 \x01(\t\"U\n\x15ListAtespacesResponse\x12#\n\tatespaces\x18\x01 \x03(\x0b\x32\x10.ateapi.Atespace\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"<\n\x15\x44\x65leteAtespaceRequest\x12#\n\x08\x61tespace\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"K\n\x1a\x43reateActorTemplateRequest\x12-\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x15.ateapi.ActorTemplate\"D\n\x17GetActorTemplateRequest\x12)\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"T\n\x19ListActorTemplatesRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"e\n\x1aListActorTemplatesResponse\x12.\n\x0f\x61\x63tor_templates\x18\x01 \x03(\x0b\x32\x15.ateapi.ActorTemplate\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"G\n\x1a\x44\x65leteActorTemplateRequest\x12)\n\x0e\x61\x63tor_template\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"3\n\x0fGetActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"2\n\x12\x43reateActorRequest\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"2\n\x12UpdateActorRequest\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"7\n\x13SuspendActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"4\n\x14SuspendActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"5\n\x11PauseActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"2\n\x12PauseActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\"D\n\x12ResumeActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x0c\n\x04\x62oot\x18\x02 \x01(\x08\"D\n\x13ResumeActorResponse\x12\x1c\n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\r.ateapi.Actor\x12\x0f\n\x07resumed\x18\x02 \x01(\x08\"I\n\x12\x44\x65leteActorRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tany_state\x18\x02 \x01(\x08\"?\n\x1bGetActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"o\n\x1e\x43reateActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12+\n\regress_policy\x18\x02 \x01(\x0b\x32\x14.ateapi.EgressPolicy\"o\n\x1eUpdateActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12+\n\regress_policy\x18\x02 \x01(\x0b\x32\x14.ateapi.EgressPolicy\"B\n\x1e\x44\x65leteActorEgressPolicyRequest\x12 \n\x05\x61\x63tor\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"\\\n\x13MintActorJWTRequest\x12 \n\x05\x61\x63tor\x18\x05 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tactor_uid\x18\x07 \x01(\t\x12\x10\n\x08\x61udience\x18\x01 \x03(\t\")\n\x14MintActorJWTResponse\x12\x11\n\tactor_jwt\x18\x01 \x01(\t\"\xa9\x01\n\x1bMintActorCertificateRequest\x12 \n\x05\x61\x63tor\x18\x06 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tactor_uid\x18\x07 \x01(\t\x12#\n\x1b\x63\x65rtificate_signing_request\x18\x02 \x01(\x0c\x12\x30\n\x07purpose\x18\x04 \x01(\x0e\x32\x1f.ateapi.ActorCertificatePurpose\":\n\x1cMintActorCertificateResponse\x12\x1a\n\x12\x61\x63tor_certificates\x18\x01 \x03(\x0c\"D\n\x17GetActorSnapshotRequest\x12)\n\x0e\x61\x63tor_snapshot\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"K\n\x1aGetActorSnapshotTagRequest\x12-\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"T\n\x19ListActorSnapshotsRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"e\n\x1aListActorSnapshotsResponse\x12.\n\x0f\x61\x63tor_snapshots\x18\x01 \x03(\x0b\x32\x15.ateapi.ActorSnapshot\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"U\n\x1d\x43reateActorSnapshotTagRequest\x12\x34\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x18.ateapi.ActorSnapshotTag\"U\n\x1dUpdateActorSnapshotTagRequest\x12\x34\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x18.ateapi.ActorSnapshotTag\"N\n\x1d\x44\x65leteActorSnapshotTagRequest\x12-\n\x12\x61\x63tor_snapshot_tag\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"-\n\rDeleteOptions\x12\x0f\n\x07version\x18\x01 \x01(\x03\x12\x0b\n\x03uid\x18\x02 \x01(\t\";\n\x12ListWorkersRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x12\n\npage_token\x18\x02 \x01(\t\"O\n\x13ListWorkersResponse\x12\x1f\n\x07workers\x18\x01 \x03(\x0b\x32\x0e.ateapi.Worker\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"5\n\x10GetWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"5\n\x13\x43reateWorkerRequest\x12\x1e\n\x06worker\x18\x01 \x01(\x0b\x32\x0e.ateapi.Worker\"5\n\x13UpdateWorkerRequest\x12\x1e\n\x06worker\x18\x01 \x01(\x0b\x32\x0e.ateapi.Worker\"`\n\x13\x44\x65leteWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12&\n\x07options\x18\x02 \x01(\x0b\x32\x15.ateapi.DeleteOptions\"7\n\x12\x44rainWorkerRequest\x12!\n\x06worker\x18\x01 \x01(\x0b\x32\x11.ateapi.ObjectRef\"L\n\x11ListActorsRequest\x12\x10\n\x08\x61tespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\"L\n\x12ListActorsResponse\x12\x1d\n\x06\x61\x63tors\x18\x01 \x03(\x0b\x32\r.ateapi.Actor\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\xf0\x02\n\x06Worker\x12*\n\x08metadata\x18\x01 \x01(\x0b\x32\x18.ateapi.ResourceMetadata\x12\x18\n\x10worker_namespace\x18\x02 \x01(\t\x12\x13\n\x0bworker_pool\x18\x03 \x01(\t\x12\x12\n\nworker_pod\x18\x04 \x01(\t\x12\x16\n\x0eworker_pod_uid\x18\x05 \x01(\t\x12\x11\n\tnode_name\x18\x06 \x01(\t\x12\n\n\x02ip\x18\x07 \x01(\t\x12\x15\n\rsandbox_class\x18\x08 \x01(\t\x12*\n\x06labels\x18\t \x03(\x0b\x32\x1a.ateapi.Worker.LabelsEntry\x12(\n\x08\x63\x61pacity\x18\n \x01(\x0b\x32\x16.ateapi.WorkerCapacity\x12$\n\x06status\x18\x0b \x01(\x0b\x32\x14.ateapi.WorkerStatus\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\x0cWorkerStatus\x12\"\n\x05state\x18\x01 \x01(\x0e\x32\x13.ateapi.WorkerState\x12+\n\nassignment\x18\x02 \x01(\x0b\x32\x17.ateapi.ActorAssignment\"9\n\x0eWorkerCapacity\x12\x11\n\tcpu_milli\x18\x01 \x01(\x03\x12\x14\n\x0cmemory_bytes\x18\x02 \x01(\x03\"u\n\x0f\x41\x63torAssignment\x12 \n\x05\x61\x63tor\x18\x02 \x01(\x0b\x32\x11.ateapi.ObjectRef\x12\x11\n\tactor_uid\x18\x03 \x01(\t\x12-\n\x12\x61\x63tor_template_ref\x18\x04 \x01(\x0b\x32\x11.ateapi.ObjectRef*\x80\x01\n\x14SnapshotContentScope\x12&\n\"SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED\x10\x00\x12\x1f\n\x1bSNAPSHOT_CONTENT_SCOPE_FULL\x10\x01\x12\x1f\n\x1bSNAPSHOT_CONTENT_SCOPE_DATA\x10\x02*\x90\x01\n\x15\x41\x63torSnapshotTagScope\x12(\n$ACTOR_SNAPSHOT_TAG_SCOPE_UNSPECIFIED\x10\x00\x12%\n!ACTOR_SNAPSHOT_TAG_SCOPE_ATESPACE\x10\x01\x12&\n\"ACTOR_SNAPSHOT_TAG_SCOPE_PUBLISHED\x10\x02*\xf7\x01\n\nActorState\x12\x1b\n\x17\x41\x43TOR_STATE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x41\x43TOR_STATE_RESUMING\x10\x01\x12\x17\n\x13\x41\x43TOR_STATE_RUNNING\x10\x02\x12\x1a\n\x16\x41\x43TOR_STATE_SUSPENDING\x10\x03\x12\x19\n\x15\x41\x43TOR_STATE_SUSPENDED\x10\x04\x12\x17\n\x13\x41\x43TOR_STATE_PAUSING\x10\x05\x12\x16\n\x12\x41\x43TOR_STATE_PAUSED\x10\x06\x12\x17\n\x13\x41\x43TOR_STATE_CRASHED\x10\x07\x12\x18\n\x14\x41\x43TOR_STATE_DELETING\x10\x08*b\n\x0cSandboxClass\x12\x1d\n\x19SANDBOX_CLASS_UNSPECIFIED\x10\x00\x12\x18\n\x14SANDBOX_CLASS_GVISOR\x10\x01\x12\x19\n\x15SANDBOX_CLASS_MICROVM\x10\x02*d\n\x0cResumeSource\x12\x1d\n\x19RESUME_SOURCE_UNSPECIFIED\x10\x00\x12\x1b\n\x17RESUME_SOURCE_COLD_BOOT\x10\x01\x12\x18\n\x14RESUME_SOURCE_GOLDEN\x10\x02*\x9a\x01\n\x12\x41\x63torMetadataField\x12$\n ACTOR_METADATA_FIELD_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x41\x43TOR_METADATA_FIELD_NAME\x10\x01\x12!\n\x1d\x41\x43TOR_METADATA_FIELD_ATESPACE\x10\x02\x12\x1c\n\x18\x41\x43TOR_METADATA_FIELD_UID\x10\x03*k\n\x17\x41\x63torCertificatePurpose\x12)\n%ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED\x10\x00\x12%\n!ACTOR_CERTIFICATE_PURPOSE_ATUNNEL\x10\x01*_\n\x0bWorkerState\x12\x1c\n\x18WORKER_STATE_UNSPECIFIED\x10\x00\x12\x17\n\x13WORKER_STATE_ACTIVE\x10\x01\x12\x19\n\x15WORKER_STATE_DRAINING\x10\x02\x32\xb6\x14\n\x07\x43ontrol\x12\x34\n\x08GetActor\x12\x17.ateapi.GetActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n\x0b\x43reateActor\x12\x1a.ateapi.CreateActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n\x0bUpdateActor\x12\x1a.ateapi.UpdateActorRequest\x1a\r.ateapi.Actor\"\x00\x12K\n\x0cSuspendActor\x12\x1b.ateapi.SuspendActorRequest\x1a\x1c.ateapi.SuspendActorResponse\"\x00\x12\x45\n\nPauseActor\x12\x19.ateapi.PauseActorRequest\x1a\x1a.ateapi.PauseActorResponse\"\x00\x12H\n\x0bResumeActor\x12\x1a.ateapi.ResumeActorRequest\x1a\x1b.ateapi.ResumeActorResponse\"\x00\x12:\n\x0b\x44\x65leteActor\x12\x1a.ateapi.DeleteActorRequest\x1a\r.ateapi.Actor\"\x00\x12S\n\x14GetActorEgressPolicy\x12#.ateapi.GetActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17\x43reateActorEgressPolicy\x12&.ateapi.CreateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17UpdateActorEgressPolicy\x12&.ateapi.UpdateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n\x17\x44\x65leteActorEgressPolicy\x12&.ateapi.DeleteActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12K\n\x0cMintActorJWT\x12\x1b.ateapi.MintActorJWTRequest\x1a\x1c.ateapi.MintActorJWTResponse\"\x00\x12\x63\n\x14MintActorCertificate\x12#.ateapi.MintActorCertificateRequest\x1a$.ateapi.MintActorCertificateResponse\"\x00\x12L\n\x10GetActorSnapshot\x12\x1f.ateapi.GetActorSnapshotRequest\x1a\x15.ateapi.ActorSnapshot\"\x00\x12U\n\x13GetActorSnapshotTag\x12\".ateapi.GetActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12]\n\x12ListActorSnapshots\x12!.ateapi.ListActorSnapshotsRequest\x1a\".ateapi.ListActorSnapshotsResponse\"\x00\x12[\n\x16\x43reateActorSnapshotTag\x12%.ateapi.CreateActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12[\n\x16UpdateActorSnapshotTag\x12%.ateapi.UpdateActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12[\n\x16\x44\x65leteActorSnapshotTag\x12%.ateapi.DeleteActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12H\n\x0bListWorkers\x12\x1a.ateapi.ListWorkersRequest\x1a\x1b.ateapi.ListWorkersResponse\"\x00\x12\x37\n\tGetWorker\x12\x18.ateapi.GetWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0c\x43reateWorker\x12\x1b.ateapi.CreateWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0cUpdateWorker\x12\x1b.ateapi.UpdateWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12=\n\x0c\x44\x65leteWorker\x12\x1b.ateapi.DeleteWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12;\n\x0b\x44rainWorker\x12\x1a.ateapi.DrainWorkerRequest\x1a\x0e.ateapi.Worker\"\x00\x12\x45\n\nListActors\x12\x19.ateapi.ListActorsRequest\x1a\x1a.ateapi.ListActorsResponse\"\x00\x12\x43\n\x0e\x43reateAtespace\x12\x1d.ateapi.CreateAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12=\n\x0bGetAtespace\x12\x1a.ateapi.GetAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12N\n\rListAtespaces\x12\x1c.ateapi.ListAtespacesRequest\x1a\x1d.ateapi.ListAtespacesResponse\"\x00\x12\x43\n\x0e\x44\x65leteAtespace\x12\x1d.ateapi.DeleteAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12R\n\x13\x43reateActorTemplate\x12\".ateapi.CreateActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12L\n\x10GetActorTemplate\x12\x1f.ateapi.GetActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12]\n\x12ListActorTemplates\x12!.ateapi.ListActorTemplatesRequest\x1a\".ateapi.ListActorTemplatesResponse\"\x00\x12R\n\x13\x44\x65leteActorTemplate\x12\".ateapi.DeleteActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x42\x39Z7github.com/agent-substrate/substrate/pkg/proto/ateapipbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -54,22 +54,22 @@ _globals['_EXTERNALVOLUME_VOLUMECONTEXTENTRY']._serialized_options = b'8\001' _globals['_WORKER_LABELSENTRY']._loaded_options = None _globals['_WORKER_LABELSENTRY']._serialized_options = b'8\001' - _globals['_SNAPSHOTCONTENTSCOPE']._serialized_start=9284 - _globals['_SNAPSHOTCONTENTSCOPE']._serialized_end=9412 - _globals['_ACTORSNAPSHOTTAGSCOPE']._serialized_start=9415 - _globals['_ACTORSNAPSHOTTAGSCOPE']._serialized_end=9559 - _globals['_ACTORSTATE']._serialized_start=9562 - _globals['_ACTORSTATE']._serialized_end=9809 - _globals['_SANDBOXCLASS']._serialized_start=9811 - _globals['_SANDBOXCLASS']._serialized_end=9909 - _globals['_RESUMESOURCE']._serialized_start=9911 - _globals['_RESUMESOURCE']._serialized_end=10011 - _globals['_ACTORMETADATAFIELD']._serialized_start=10014 - _globals['_ACTORMETADATAFIELD']._serialized_end=10168 - _globals['_WORKERSTATE']._serialized_start=10170 - _globals['_WORKERSTATE']._serialized_end=10265 - _globals['_ACTORCERTIFICATEPURPOSE']._serialized_start=10267 - _globals['_ACTORCERTIFICATEPURPOSE']._serialized_end=10374 + _globals['_SNAPSHOTCONTENTSCOPE']._serialized_start=9304 + _globals['_SNAPSHOTCONTENTSCOPE']._serialized_end=9432 + _globals['_ACTORSNAPSHOTTAGSCOPE']._serialized_start=9435 + _globals['_ACTORSNAPSHOTTAGSCOPE']._serialized_end=9579 + _globals['_ACTORSTATE']._serialized_start=9582 + _globals['_ACTORSTATE']._serialized_end=9829 + _globals['_SANDBOXCLASS']._serialized_start=9831 + _globals['_SANDBOXCLASS']._serialized_end=9929 + _globals['_RESUMESOURCE']._serialized_start=9931 + _globals['_RESUMESOURCE']._serialized_end=10031 + _globals['_ACTORMETADATAFIELD']._serialized_start=10034 + _globals['_ACTORMETADATAFIELD']._serialized_end=10188 + _globals['_ACTORCERTIFICATEPURPOSE']._serialized_start=10190 + _globals['_ACTORCERTIFICATEPURPOSE']._serialized_end=10297 + _globals['_WORKERSTATE']._serialized_start=10299 + _globals['_WORKERSTATE']._serialized_end=10394 _globals['_LOCALSNAPSHOTINFO']._serialized_start=87 _globals['_LOCALSNAPSHOTINFO']._serialized_end=221 _globals['_SELECTOR']._serialized_start=223 @@ -210,60 +210,58 @@ _globals['_UPDATEACTOREGRESSPOLICYREQUEST']._serialized_end=6963 _globals['_DELETEACTOREGRESSPOLICYREQUEST']._serialized_start=6965 _globals['_DELETEACTOREGRESSPOLICYREQUEST']._serialized_end=7031 - _globals['_GETACTORSNAPSHOTREQUEST']._serialized_start=7033 - _globals['_GETACTORSNAPSHOTREQUEST']._serialized_end=7101 - _globals['_GETACTORSNAPSHOTTAGREQUEST']._serialized_start=7103 - _globals['_GETACTORSNAPSHOTTAGREQUEST']._serialized_end=7178 - _globals['_LISTACTORSNAPSHOTSREQUEST']._serialized_start=7180 - _globals['_LISTACTORSNAPSHOTSREQUEST']._serialized_end=7264 - _globals['_LISTACTORSNAPSHOTSRESPONSE']._serialized_start=7266 - _globals['_LISTACTORSNAPSHOTSRESPONSE']._serialized_end=7367 - _globals['_CREATEACTORSNAPSHOTTAGREQUEST']._serialized_start=7369 - _globals['_CREATEACTORSNAPSHOTTAGREQUEST']._serialized_end=7454 - _globals['_UPDATEACTORSNAPSHOTTAGREQUEST']._serialized_start=7456 - _globals['_UPDATEACTORSNAPSHOTTAGREQUEST']._serialized_end=7541 - _globals['_DELETEACTORSNAPSHOTTAGREQUEST']._serialized_start=7543 - _globals['_DELETEACTORSNAPSHOTTAGREQUEST']._serialized_end=7621 - _globals['_DELETEOPTIONS']._serialized_start=7623 - _globals['_DELETEOPTIONS']._serialized_end=7668 - _globals['_LISTWORKERSREQUEST']._serialized_start=7670 - _globals['_LISTWORKERSREQUEST']._serialized_end=7729 - _globals['_LISTWORKERSRESPONSE']._serialized_start=7731 - _globals['_LISTWORKERSRESPONSE']._serialized_end=7810 - _globals['_GETWORKERREQUEST']._serialized_start=7812 - _globals['_GETWORKERREQUEST']._serialized_end=7865 - _globals['_CREATEWORKERREQUEST']._serialized_start=7867 - _globals['_CREATEWORKERREQUEST']._serialized_end=7920 - _globals['_UPDATEWORKERREQUEST']._serialized_start=7922 - _globals['_UPDATEWORKERREQUEST']._serialized_end=7975 - _globals['_DELETEWORKERREQUEST']._serialized_start=7977 - _globals['_DELETEWORKERREQUEST']._serialized_end=8073 - _globals['_DRAINWORKERREQUEST']._serialized_start=8075 - _globals['_DRAINWORKERREQUEST']._serialized_end=8130 - _globals['_LISTACTORSREQUEST']._serialized_start=8132 - _globals['_LISTACTORSREQUEST']._serialized_end=8208 - _globals['_LISTACTORSRESPONSE']._serialized_start=8210 - _globals['_LISTACTORSRESPONSE']._serialized_end=8286 - _globals['_WORKER']._serialized_start=8289 - _globals['_WORKER']._serialized_end=8657 - _globals['_WORKER_LABELSENTRY']._serialized_start=8612 - _globals['_WORKER_LABELSENTRY']._serialized_end=8657 - _globals['_WORKERSTATUS']._serialized_start=8659 - _globals['_WORKERSTATUS']._serialized_end=8754 - _globals['_WORKERCAPACITY']._serialized_start=8756 - _globals['_WORKERCAPACITY']._serialized_end=8813 - _globals['_ACTORASSIGNMENT']._serialized_start=8815 - _globals['_ACTORASSIGNMENT']._serialized_end=8932 - _globals['_MINTJWTREQUEST']._serialized_start=8934 - _globals['_MINTJWTREQUEST']._serialized_end=9025 - _globals['_MINTJWTRESPONSE']._serialized_start=9027 - _globals['_MINTJWTRESPONSE']._serialized_end=9063 - _globals['_MINTCERTREQUEST']._serialized_start=9066 - _globals['_MINTCERTREQUEST']._serialized_end=9233 - _globals['_MINTCERTRESPONSE']._serialized_start=9235 - _globals['_MINTCERTRESPONSE']._serialized_end=9281 - _globals['_CONTROL']._serialized_start=10377 - _globals['_CONTROL']._serialized_end=12813 - _globals['_ACTORIDENTITY']._serialized_start=12816 - _globals['_ACTORIDENTITY']._serialized_end=12954 + _globals['_MINTACTORJWTREQUEST']._serialized_start=7033 + _globals['_MINTACTORJWTREQUEST']._serialized_end=7125 + _globals['_MINTACTORJWTRESPONSE']._serialized_start=7127 + _globals['_MINTACTORJWTRESPONSE']._serialized_end=7168 + _globals['_MINTACTORCERTIFICATEREQUEST']._serialized_start=7171 + _globals['_MINTACTORCERTIFICATEREQUEST']._serialized_end=7340 + _globals['_MINTACTORCERTIFICATERESPONSE']._serialized_start=7342 + _globals['_MINTACTORCERTIFICATERESPONSE']._serialized_end=7400 + _globals['_GETACTORSNAPSHOTREQUEST']._serialized_start=7402 + _globals['_GETACTORSNAPSHOTREQUEST']._serialized_end=7470 + _globals['_GETACTORSNAPSHOTTAGREQUEST']._serialized_start=7472 + _globals['_GETACTORSNAPSHOTTAGREQUEST']._serialized_end=7547 + _globals['_LISTACTORSNAPSHOTSREQUEST']._serialized_start=7549 + _globals['_LISTACTORSNAPSHOTSREQUEST']._serialized_end=7633 + _globals['_LISTACTORSNAPSHOTSRESPONSE']._serialized_start=7635 + _globals['_LISTACTORSNAPSHOTSRESPONSE']._serialized_end=7736 + _globals['_CREATEACTORSNAPSHOTTAGREQUEST']._serialized_start=7738 + _globals['_CREATEACTORSNAPSHOTTAGREQUEST']._serialized_end=7823 + _globals['_UPDATEACTORSNAPSHOTTAGREQUEST']._serialized_start=7825 + _globals['_UPDATEACTORSNAPSHOTTAGREQUEST']._serialized_end=7910 + _globals['_DELETEACTORSNAPSHOTTAGREQUEST']._serialized_start=7912 + _globals['_DELETEACTORSNAPSHOTTAGREQUEST']._serialized_end=7990 + _globals['_DELETEOPTIONS']._serialized_start=7992 + _globals['_DELETEOPTIONS']._serialized_end=8037 + _globals['_LISTWORKERSREQUEST']._serialized_start=8039 + _globals['_LISTWORKERSREQUEST']._serialized_end=8098 + _globals['_LISTWORKERSRESPONSE']._serialized_start=8100 + _globals['_LISTWORKERSRESPONSE']._serialized_end=8179 + _globals['_GETWORKERREQUEST']._serialized_start=8181 + _globals['_GETWORKERREQUEST']._serialized_end=8234 + _globals['_CREATEWORKERREQUEST']._serialized_start=8236 + _globals['_CREATEWORKERREQUEST']._serialized_end=8289 + _globals['_UPDATEWORKERREQUEST']._serialized_start=8291 + _globals['_UPDATEWORKERREQUEST']._serialized_end=8344 + _globals['_DELETEWORKERREQUEST']._serialized_start=8346 + _globals['_DELETEWORKERREQUEST']._serialized_end=8442 + _globals['_DRAINWORKERREQUEST']._serialized_start=8444 + _globals['_DRAINWORKERREQUEST']._serialized_end=8499 + _globals['_LISTACTORSREQUEST']._serialized_start=8501 + _globals['_LISTACTORSREQUEST']._serialized_end=8577 + _globals['_LISTACTORSRESPONSE']._serialized_start=8579 + _globals['_LISTACTORSRESPONSE']._serialized_end=8655 + _globals['_WORKER']._serialized_start=8658 + _globals['_WORKER']._serialized_end=9026 + _globals['_WORKER_LABELSENTRY']._serialized_start=8981 + _globals['_WORKER_LABELSENTRY']._serialized_end=9026 + _globals['_WORKERSTATUS']._serialized_start=9028 + _globals['_WORKERSTATUS']._serialized_end=9123 + _globals['_WORKERCAPACITY']._serialized_start=9125 + _globals['_WORKERCAPACITY']._serialized_end=9182 + _globals['_ACTORASSIGNMENT']._serialized_start=9184 + _globals['_ACTORASSIGNMENT']._serialized_end=9301 + _globals['_CONTROL']._serialized_start=10397 + _globals['_CONTROL']._serialized_end=13011 # @@protoc_insertion_point(module_scope) diff --git a/benchmarking/locust/common/ateapi_pb2_grpc.py b/benchmarking/locust/common/ateapi_pb2_grpc.py index 37a0047a23..2da35dae16 100644 --- a/benchmarking/locust/common/ateapi_pb2_grpc.py +++ b/benchmarking/locust/common/ateapi_pb2_grpc.py @@ -104,6 +104,16 @@ def __init__(self, channel): request_serializer=ateapi__pb2.DeleteActorEgressPolicyRequest.SerializeToString, response_deserializer=ateapi__pb2.EgressPolicy.FromString, _registered_method=True) + self.MintActorJWT = channel.unary_unary( + '/ateapi.Control/MintActorJWT', + request_serializer=ateapi__pb2.MintActorJWTRequest.SerializeToString, + response_deserializer=ateapi__pb2.MintActorJWTResponse.FromString, + _registered_method=True) + self.MintActorCertificate = channel.unary_unary( + '/ateapi.Control/MintActorCertificate', + request_serializer=ateapi__pb2.MintActorCertificateRequest.SerializeToString, + response_deserializer=ateapi__pb2.MintActorCertificateResponse.FromString, + _registered_method=True) self.GetActorSnapshot = channel.unary_unary( '/ateapi.Control/GetActorSnapshot', request_serializer=ateapi__pb2.GetActorSnapshotRequest.SerializeToString, @@ -294,6 +304,28 @@ def DeleteActorEgressPolicy(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def MintActorJWT(self, request, context): + """Create a Substrate-issued JWT asserting the actor identity. + + * Called by the egress gateway when actor JWT injection is configured for outbound requests. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def MintActorCertificate(self, request, context): + """Create a Substrate-issued SPIFFE certificate asserting the actor identity. + + * Called by atelet to provision an atunnel with a certificate for + communication with the egress gateway. TODO(ahmedtd): Migrate this use + case to a distinct certificate to prevent actor/atunnel confusion. + * Called by the egress gateway when actor client certificate injection is + configured for outbound requests. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetActorSnapshot(self, request, context): """Get an ActorSnapshot. """ @@ -502,6 +534,16 @@ def add_ControlServicer_to_server(servicer, server): request_deserializer=ateapi__pb2.DeleteActorEgressPolicyRequest.FromString, response_serializer=ateapi__pb2.EgressPolicy.SerializeToString, ), + 'MintActorJWT': grpc.unary_unary_rpc_method_handler( + servicer.MintActorJWT, + request_deserializer=ateapi__pb2.MintActorJWTRequest.FromString, + response_serializer=ateapi__pb2.MintActorJWTResponse.SerializeToString, + ), + 'MintActorCertificate': grpc.unary_unary_rpc_method_handler( + servicer.MintActorCertificate, + request_deserializer=ateapi__pb2.MintActorCertificateRequest.FromString, + response_serializer=ateapi__pb2.MintActorCertificateResponse.SerializeToString, + ), 'GetActorSnapshot': grpc.unary_unary_rpc_method_handler( servicer.GetActorSnapshot, request_deserializer=ateapi__pb2.GetActorSnapshotRequest.FromString, @@ -916,6 +958,60 @@ def DeleteActorEgressPolicy(request, metadata, _registered_method=True) + @staticmethod + def MintActorJWT(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/ateapi.Control/MintActorJWT', + ateapi__pb2.MintActorJWTRequest.SerializeToString, + ateapi__pb2.MintActorJWTResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def MintActorCertificate(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/ateapi.Control/MintActorCertificate', + ateapi__pb2.MintActorCertificateRequest.SerializeToString, + ateapi__pb2.MintActorCertificateResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def GetActorSnapshot(request, target, @@ -1482,151 +1578,3 @@ def DeleteActorTemplate(request, timeout, metadata, _registered_method=True) - - -class ActorIdentityStub: - """ActorIdentity allows substrate workloads to exchange their - infrastructure-level credentials (k8s service account token, etc.) for a - substrate actor-level credential. A given substrate actor might migrate - between many different physical workers over the course of its lifecycle, - whereas the actor credential's identity will be stable for the life of the - actor. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.MintJWT = channel.unary_unary( - '/ateapi.ActorIdentity/MintJWT', - request_serializer=ateapi__pb2.MintJWTRequest.SerializeToString, - response_deserializer=ateapi__pb2.MintJWTResponse.FromString, - _registered_method=True) - self.MintCert = channel.unary_unary( - '/ateapi.ActorIdentity/MintCert', - request_serializer=ateapi__pb2.MintCertRequest.SerializeToString, - response_deserializer=ateapi__pb2.MintCertResponse.FromString, - _registered_method=True) - - -class ActorIdentityServicer: - """ActorIdentity allows substrate workloads to exchange their - infrastructure-level credentials (k8s service account token, etc.) for a - substrate actor-level credential. A given substrate actor might migrate - between many different physical workers over the course of its lifecycle, - whereas the actor credential's identity will be stable for the life of the - actor. - """ - - def MintJWT(self, request, context): - """Request an Actor Identity JWT. - - To call this RPC, you must be authenticated as the Kubernetes Pod that is - currently running the requested actor. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MintCert(self, request, context): - """Request an Actor Identity Certificate for an actor. - - Actors do not call this RPC themselves. The atelet hosting the actor calls - it on the actor's behalf, authenticating with its own client certificate - rather than a bearer token. - - Authorization is decided on that client certificate and the worker - identity attested by atelet. Ateapi verifies that the worker is assigned to - the actor and that the actor points back to that exact worker before signing. - - The certificate in the response is the actor's identity, not the atelet's. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_ActorIdentityServicer_to_server(servicer, server): - rpc_method_handlers = { - 'MintJWT': grpc.unary_unary_rpc_method_handler( - servicer.MintJWT, - request_deserializer=ateapi__pb2.MintJWTRequest.FromString, - response_serializer=ateapi__pb2.MintJWTResponse.SerializeToString, - ), - 'MintCert': grpc.unary_unary_rpc_method_handler( - servicer.MintCert, - request_deserializer=ateapi__pb2.MintCertRequest.FromString, - response_serializer=ateapi__pb2.MintCertResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'ateapi.ActorIdentity', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('ateapi.ActorIdentity', rpc_method_handlers) - - - # This class is part of an EXPERIMENTAL API. -class ActorIdentity: - """ActorIdentity allows substrate workloads to exchange their - infrastructure-level credentials (k8s service account token, etc.) for a - substrate actor-level credential. A given substrate actor might migrate - between many different physical workers over the course of its lifecycle, - whereas the actor credential's identity will be stable for the life of the - actor. - """ - - @staticmethod - def MintJWT(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/ateapi.ActorIdentity/MintJWT', - ateapi__pb2.MintJWTRequest.SerializeToString, - ateapi__pb2.MintJWTResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def MintCert(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/ateapi.ActorIdentity/MintCert', - ateapi__pb2.MintCertRequest.SerializeToString, - ateapi__pb2.MintCertResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) diff --git a/cmd/ate-setup/internal/steps/create.go b/cmd/ate-setup/internal/steps/create.go index 9add907961..0c9c9574b0 100644 --- a/cmd/ate-setup/internal/steps/create.go +++ b/cmd/ate-setup/internal/steps/create.go @@ -255,8 +255,9 @@ func (e *Env) createJWTPool(ctx context.Context, namespace, name string) error { if err != nil { return fmt.Errorf("while generating the JWT authority for %s/%s: %w", namespace, name, err) } - poolBytes, err := localjwtauthority.Marshal(&localjwtauthority.Pool{ - Authorities: []*localjwtauthority.Authority{authority}, + poolBytes, err := localjwtauthority.Marshal(&localjwtauthority.ConcretePool{ + Authorities: []*localjwtauthority.Authority{authority}, + ActiveForSigning: poolKeyID, }) if err != nil { return fmt.Errorf("while marshaling the JWT pool for %s/%s: %w", namespace, name, err) diff --git a/cmd/ateapi/internal/actoridentity/actoridentity.go b/cmd/ateapi/internal/actoridentity/actoridentity.go index 7304c43d15..f337dedcf7 100644 --- a/cmd/ateapi/internal/actoridentity/actoridentity.go +++ b/cmd/ateapi/internal/actoridentity/actoridentity.go @@ -23,14 +23,13 @@ import ( "fmt" "log/slog" "net/url" - "os" "path" "time" - "github.com/agent-substrate/substrate/cmd/ateapi/internal/actoridjwt" "github.com/agent-substrate/substrate/cmd/ateapi/internal/controlapi" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" "github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache" + "github.com/agent-substrate/substrate/internal/actoridjwt" "github.com/agent-substrate/substrate/internal/localca" "github.com/agent-substrate/substrate/internal/localjwtauthority" "github.com/agent-substrate/substrate/internal/principal" @@ -49,11 +48,12 @@ import ( type Server struct { ateapipb.UnimplementedActorIdentityServer + // TODO(identity): Issuer is probably logically a property of the JWT + // signing pool. actorIdentityJWTIssuer string - // TODO: Cache the signing keys in memory, so we don't read from a file every time. - actorIDJWTPoolFile string - actorIDCAPool localca.Pool + actorIDJWTPool localjwtauthority.Pool + actorIDCAPool localca.Pool // store is the actor database. MintCert consults it to confirm the caller // is entitled to the actor it is asking for a credential for. @@ -63,10 +63,10 @@ type Server struct { var _ ateapipb.ActorIdentityServer = (*Server)(nil) -func New(actorIdentityJWTIssuer, actorIDJWTPoolFile string, actorIDCAPool localca.Pool, store store.Interface, workers *workercache.Cache) *Server { +func New(actorIdentityJWTIssuer string, actorIDJWTPool localjwtauthority.Pool, actorIDCAPool localca.Pool, store store.Interface, workers *workercache.Cache) *Server { return &Server{ actorIdentityJWTIssuer: actorIdentityJWTIssuer, - actorIDJWTPoolFile: actorIDJWTPoolFile, + actorIDJWTPool: actorIDJWTPool, actorIDCAPool: actorIDCAPool, store: store, workers: workers, @@ -102,15 +102,9 @@ func (s *Server) MintJWT(ctx context.Context, req *ateapipb.MintJWTRequest) (*at // TODO: Cross-check the verified caller and requested actor against the actor database. - // TODO: Cache signing keys in memory, so we don't read from disk every time. - signingPoolBytes, err := os.ReadFile(s.actorIDJWTPoolFile) - if err != nil { - return nil, fmt.Errorf("while reading signing pool bytes: %w", err) - } - - signingPool, err := localjwtauthority.Unmarshal(signingPoolBytes) - if err != nil { - return nil, fmt.Errorf("while unmarshaling signing pool: %w", err) + // We only issue tokens with audience bindings. + if len(req.GetAudience()) == 0 { + return nil, fmt.Errorf("at least one audience must be requested") } actorClaims := &actoridjwt.Claims{ @@ -131,13 +125,7 @@ func (s *Server) MintJWT(ctx context.Context, req *ateapipb.MintJWTRequest) (*at }, } - actorWireClaims, err := actoridjwt.ClaimsToWire(actorClaims) - if err != nil { - return nil, fmt.Errorf("while making actor JWT claims: %w", err) - } - - // Assume the first authority is the one to use for signing. - actorJWT, err := actoridjwt.Sign(actorWireClaims, signingPool.Authorities[0].SigningKey, signingPool.Authorities[0].Algorithm, signingPool.Authorities[0].ID) + actorJWT, err := s.actorIDJWTPool.SignJWT(actorClaims) if err != nil { return nil, fmt.Errorf("while signing actor JWT: %w", err) } diff --git a/cmd/ateapi/internal/actoridentity/actoridentity_test.go b/cmd/ateapi/internal/actoridentity/actoridentity_test.go deleted file mode 100644 index d1282de67f..0000000000 --- a/cmd/ateapi/internal/actoridentity/actoridentity_test.go +++ /dev/null @@ -1,1084 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package actoridentity - -import ( - "context" - "crypto/ed25519" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - "math/big" - "net/url" - "path" - "strings" - "testing" - "time" - - "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" - "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest" - "github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache" - "github.com/agent-substrate/substrate/internal/localca" - "github.com/agent-substrate/substrate/internal/principal" - "github.com/agent-substrate/substrate/internal/resources" - "github.com/agent-substrate/substrate/internal/substratex509" - "github.com/agent-substrate/substrate/pkg/proto/ateapipb" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/status" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -func assertValidateErr(t *testing.T, got field.ErrorList, want field.ErrorList) { - t.Helper() - field.ErrorMatcher{}.ByType().ByField().ByOrigin().Test(t, want, got) -} - -const ( - testAtespace = "team-alpha" - testActorName = "counter-1" - testPodNS = "ate-workers" - testWorkerPod = "worker-abc" - // testWorkerName is the seeded worker's resource name, and so the name - // MintCert requests reference it by. It is deliberately not equal to - // testWorkerPodUID: MintCert must resolve the worker by name alone. - testWorkerName = "5b1e0c7a-8d34-4f62-b0a9-1e7c4d29f350" - testWorkerPodUID = "e2c40f8b-71d9-4a35-8c6e-b04f9d1a7263" - testPool = "pool-1" - testNode = "node-a" - testOtherNode = "node-b" -) - -// newTestCert builds a self-signed leaf carrying the given SPIFFE URI path -// (skipped when empty) and, when podIdentity is non-nil, a PodIdentity -// extension. -// -// The certificate is created and then re-parsed on purpose: -// AddPodIdentityToCertificate writes to ExtraExtensions, but -// PodIdentityFromCertificate reads Extensions, which only x509.ParseCertificate -// populates. Self-signing is sufficient because the code under test reads an -// already transport-verified peer certificate and never re-validates the chain -// itself. -func newTestCert(t *testing.T, spiffePath string, podIdentity *substratex509.PodIdentity) *x509.Certificate { - t.Helper() - - pub, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("generate key: %v", err) - } - - template := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{CommonName: "test-caller"}, - NotBefore: time.Now().Add(-time.Hour), - NotAfter: time.Now().Add(time.Hour), - } - if spiffePath != "" { - template.URIs = []*url.URL{{Scheme: "spiffe", Host: ateletTrustDomain, Path: spiffePath}} - } - if podIdentity != nil { - if err := substratex509.AddPodIdentityToCertificate(podIdentity, template); err != nil { - t.Fatalf("add pod identity: %v", err) - } - } - - der, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv) - if err != nil { - t.Fatalf("create certificate: %v", err) - } - cert, err := x509.ParseCertificate(der) - if err != nil { - t.Fatalf("parse certificate: %v", err) - } - return cert -} - -// podIdentityOn returns a well-formed atelet PodIdentity pinned to nodeName. -func podIdentityOn(nodeName string) *substratex509.PodIdentity { - return &substratex509.PodIdentity{ - Namespace: ateletNamespace, - ServiceAccountName: ateletSA, - ServiceAccountUID: "sa-uid", - PodName: "atelet-xyz", - PodUID: "pod-uid", - NodeName: nodeName, - NodeUID: "node-uid", - } -} - -// ateletCertOn returns the certificate of the atelet running on nodeName. -func ateletCertOn(t *testing.T, nodeName string) *x509.Certificate { - t.Helper() - return newTestCert(t, path.Join("ns", ateletNamespace, "sa", ateletSA), podIdentityOn(nodeName)) -} - -// ctxWithCert injects cert as the transport-authenticated peer certificate. -// A nil cert yields a context with no peer information at all, which is what -// an unauthenticated call looks like. -func ctxWithCert(cert *x509.Certificate) context.Context { - ctx := context.Background() - if cert == nil { - return ctx - } - return peer.NewContext(ctx, &peer.Peer{ - AuthInfo: credentials.TLSInfo{ - State: tls.ConnectionState{PeerCertificates: []*x509.Certificate{cert}}, - }, - }) -} - -// newTestServer returns a Server backed by st, with a freshly generated actor -// CA pool written to a temp file. -func newTestServer(t *testing.T, st store.Interface) *Server { - t.Helper() - - ca, err := localca.GenerateCA("test-actor-ca", localca.KeyTypeED25519, 24*time.Hour) - if err != nil { - t.Fatalf("generate CA: %v", err) - } - pool := &localca.ConcretePool{ - CAs: []*localca.CA{ca}, - ActiveForSigning: "test-actor-ca", - } - - var workers *workercache.Cache - if st != nil { - workers = workercache.New(st, time.Hour) - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - if err := workers.Start(ctx); err != nil { - t.Fatalf("start worker cache: %v", err) - } - } - return New("issuer", "", pool, st, workers) -} - -// staleWatchStore wraps a store with a WatchWorkers that never delivers, -// freezing any workercache built over it at its seed-time state — the unit -// analog of the watch's delivery latency. -type staleWatchStore struct{ store.Interface } - -func (s staleWatchStore) WatchWorkers(context.Context) (*store.WorkerWatch, error) { - return store.NewWorkerWatch(make(chan store.WorkerEvent), func() {}), nil -} - -// TestMintCertReadsThroughStaleWorkerCache pins the authorization -// read-through: an atelet minting immediately after ResumeActor committed the -// worker's assignment must be authorized from the store even though this -// replica's cache has not yet seen the assignment. The control case keeps the -// store unassigned too and must still deny — only fresh data may authorize, -// and only fresh data may deny. -func TestMintCertReadsThroughStaleWorkerCache(t *testing.T) { - for name, assignInStore := range map[string]bool{ - "assignment committed but not yet in cache: authorized via read-through": true, - "unassigned in cache and store: denial stands": false, - } { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - // Phase 1: worker exists, unassigned; the cache seeds this view and - // (via the inert watch) never learns anything newer. - seedActor(t, ctx, st, actorFixture{state: ateapipb.ActorState_ACTOR_STATE_RUNNING, workerNode: testNode, unassigned: true}) - workers := workercache.New(staleWatchStore{st}, time.Hour) - cacheCtx, cancel := context.WithCancel(ctx) - t.Cleanup(cancel) - if err := workers.Start(cacheCtx); err != nil { - t.Fatalf("start worker cache: %v", err) - } - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatalf("read seeded actor: %v", err) - } - if assignInStore { - // Phase 2: commit the assignment to the store only, as - // AssignWorker does (possibly on another replica). - worker, err := st.GetWorker(ctx, testWorkerName) - if err != nil { - t.Fatalf("read seeded worker: %v", err) - } - _, err = st.UpdateWorker(ctx, testWorkerName, store.PreconditionFrom(worker), func(toUpdate *ateapipb.Worker) error { - if toUpdate.Status == nil { - toUpdate.Status = &ateapipb.WorkerStatus{} - } - toUpdate.Status.Assignment = &ateapipb.ActorAssignment{ - Actor: (resources.ActorRef{Atespace: testAtespace, Name: testActorName}).ToObjectRef(), - ActorUid: actor.GetMetadata().GetUid(), - } - return nil - }) - if err != nil { - t.Fatalf("assign worker in store: %v", err) - } - } - - srv := newTestServerWithCache(t, st, workers) - resp, err := srv.MintCert(ctxWithCert(ateletCertOn(t, testNode)), mintCertRequest(t, actor.GetMetadata().GetUid())) - - wantCode := codes.PermissionDenied - if assignInStore { - wantCode = codes.OK - } - if got := status.Code(err); got != wantCode { - t.Fatalf("MintCert() code = %v (err = %v), want %v", got, err, wantCode) - } - if assignInStore && len(resp.GetActorCertificates()) == 0 { - t.Fatal("MintCert() returned no certificates") - } - }) - } -} - -// TestMintCertReadsThroughWorkerCacheMiss pins the read-through for a worker -// the cache has never seen: a worker registered moments before assignment may -// be committed to the store (possibly by another replica) before this -// replica's cache has received the worker row at all. Absence from the cache -// is stale data and must not deny by itself; absence from the store must. -func TestMintCertReadsThroughWorkerCacheMiss(t *testing.T) { - for name, workerInStore := range map[string]bool{ - "worker assigned in store but not yet in cache: authorized via read-through": true, - "worker in neither cache nor store: denial stands": false, - } { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - // Phase 1: only the actor exists; the cache seeds with no workers - // and (via the inert watch) never learns of any. - seedActor(t, ctx, st, actorFixture{state: ateapipb.ActorState_ACTOR_STATE_RUNNING, workerNode: testNode, noWorker: true}) - workers := workercache.New(staleWatchStore{st}, time.Hour) - cacheCtx, cancel := context.WithCancel(ctx) - t.Cleanup(cancel) - if err := workers.Start(cacheCtx); err != nil { - t.Fatalf("start worker cache: %v", err) - } - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatalf("read seeded actor: %v", err) - } - if workerInStore { - // Phase 2: register and assign the worker in the store only, - // after the cache stopped listening. - if _, err := st.CreateWorker(ctx, &ateapipb.Worker{ - Metadata: &ateapipb.ResourceMetadata{Name: testWorkerName}, - WorkerNamespace: testPodNS, - WorkerPool: testPool, - WorkerPod: testWorkerPod, - WorkerPodUid: testWorkerPodUID, - NodeName: testNode, - Status: &ateapipb.WorkerStatus{ - State: ateapipb.WorkerState_WORKER_STATE_ACTIVE, - Assignment: &ateapipb.ActorAssignment{ - Actor: (resources.ActorRef{Atespace: testAtespace, Name: testActorName}).ToObjectRef(), - ActorUid: actor.GetMetadata().GetUid(), - }, - }, - }); err != nil { - t.Fatalf("register worker in store: %v", err) - } - } - - srv := newTestServerWithCache(t, st, workers) - resp, err := srv.MintCert(ctxWithCert(ateletCertOn(t, testNode)), mintCertRequest(t, actor.GetMetadata().GetUid())) - - wantCode := codes.PermissionDenied - if workerInStore { - wantCode = codes.OK - } - if got := status.Code(err); got != wantCode { - t.Fatalf("MintCert() code = %v (err = %v), want %v", got, err, wantCode) - } - if workerInStore && len(resp.GetActorCertificates()) == 0 { - t.Fatal("MintCert() returned no certificates") - } - }) - } -} - -// newTestServerWithCache is newTestServer with a caller-controlled worker -// cache (e.g. one frozen at a stale state). -func newTestServerWithCache(t *testing.T, st store.Interface, workers *workercache.Cache) *Server { - t.Helper() - - ca, err := localca.GenerateCA("test-actor-ca", localca.KeyTypeED25519, 24*time.Hour) - if err != nil { - t.Fatalf("generate CA: %v", err) - } - pool := &localca.ConcretePool{CAs: []*localca.CA{ca}} - return New("issuer", "", pool, st, workers) -} - -func TestMintJWTRequiresConfiguredJWTProvider(t *testing.T) { - srv := &Server{actorIdentityJWTIssuer: "https://kubernetes.example"} - for _, tt := range []struct { - name string - ctx context.Context - code codes.Code - }{ - {name: "no principal", ctx: context.Background(), code: codes.Unauthenticated}, - { - name: "mTLS principal", - ctx: principal.InjectContext(context.Background(), principal.PrincipalInfo{ID: "spiffe://caller", Kind: principal.KindMTLS}), - code: codes.Unauthenticated, - }, - { - name: "different JWT provider", - ctx: principal.InjectContext(context.Background(), principal.PrincipalInfo{ID: "user", Kind: principal.KindJWT, Issuer: "https://accounts.google.com"}), - code: codes.PermissionDenied, - }, - } { - t.Run(tt.name, func(t *testing.T) { - _, err := srv.MintJWT(tt.ctx, &ateapipb.MintJWTRequest{}) - if got := status.Code(err); got != tt.code { - t.Fatalf("MintJWT() code = %v, want %v (err = %v)", got, tt.code, err) - } - }) - } -} - -// newCSR returns a DER-encoded, correctly self-signed CSR. -func newCSR(t *testing.T) []byte { - t.Helper() - _, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("generate key: %v", err) - } - der, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: "actor"}, - }, priv) - if err != nil { - t.Fatalf("create CSR: %v", err) - } - return der -} - -func mintCertRequest(t *testing.T, actorUID string) *ateapipb.MintCertRequest { - t.Helper() - return &ateapipb.MintCertRequest{ - Worker: &ateapipb.ObjectRef{Name: testWorkerName}, - ExpectedActorUid: actorUID, - CertificateSigningRequest: newCSR(t), - Purpose: ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL, - } -} - -// actorFixture describes the actor/worker pair seeded into the store. -type actorFixture struct { - state ateapipb.ActorState - workerNode string - // actorWorkerName overrides the Worker the actor points at while leaving - // the requesting worker unchanged, simulating a stale reciprocal - // assignment. - actorWorkerName string - // assignedTo overrides the actor the worker claims to be hosting. The zero - // value means the worker is assigned to the seeded actor. - assignedTo resources.ActorRef - // unassigned seeds the worker with no assignment at all, as pause, suspend - // and crash leave it once they have released it. - unassigned bool - // noPlacement seeds the actor with no worker assignment. - noPlacement bool - // noWorker skips seeding the worker record entirely. - noWorker bool - // mismatchedUID simulates a worker assigned to an actor with the same name/atespace but a different UID. - mismatchedUID bool -} - -// seedActor writes an actor, and normally its hosting worker, into st. -func seedActor(t *testing.T, ctx context.Context, st store.Interface, f actorFixture) { - t.Helper() - - actorRef := resources.ActorRef{Atespace: testAtespace, Name: testActorName} - - actor := &ateapipb.Actor{ - Metadata: &ateapipb.ResourceMetadata{Atespace: actorRef.Atespace, Name: actorRef.Name}, - Status: &ateapipb.ActorStatus{State: f.state}, - ActorTemplate: &ateapipb.ObjectRef{Atespace: "ate-demo", Name: "counter"}, - } - if !f.noPlacement { - workerName := testWorkerName - if f.actorWorkerName != "" { - workerName = f.actorWorkerName - } - actor.Status.WorkerAssignment = &ateapipb.WorkerAssignment{ - Worker: &ateapipb.ObjectRef{Name: workerName}, - WorkerNamespace: testPodNS, - WorkerPool: testPool, - WorkerPod: testWorkerPod, - WorkerPodUid: testWorkerPodUID, - } - } - created := storetest.MustCreateActor(t, ctx, st, actor) - - if f.noWorker { - return - } - assigned := f.assignedTo - if assigned == (resources.ActorRef{}) { - assigned = actorRef - } - assignedActorUID := created.GetMetadata().GetUid() - if f.mismatchedUID || assigned != actorRef { - assignedActorUID = "other-actor-uid" - } - worker := &ateapipb.Worker{ - Metadata: &ateapipb.ResourceMetadata{Name: testWorkerName}, - WorkerNamespace: testPodNS, - WorkerPool: testPool, - WorkerPod: testWorkerPod, - WorkerPodUid: testWorkerPodUID, - NodeName: f.workerNode, - Status: &ateapipb.WorkerStatus{ - State: ateapipb.WorkerState_WORKER_STATE_ACTIVE, - Assignment: &ateapipb.ActorAssignment{ - Actor: assigned.ToObjectRef(), - ActorUid: assignedActorUID, - }, - }, - } - if f.unassigned { - worker.Status.Assignment = nil - } - if _, err := st.CreateWorker(ctx, worker); err != nil { - t.Fatalf("seed worker: %v", err) - } -} - -// runningOnNode is the fixture for a healthy actor hosted on nodeName. -func runningOnNode(nodeName string) actorFixture { - return actorFixture{state: ateapipb.ActorState_ACTOR_STATE_RUNNING, workerNode: nodeName} -} - -// TestMintCertAuthorization covers the gate deciding whether a caller may mint -// a certificate for the requested actor. -func TestMintCertAuthorization(t *testing.T) { - // ptr is needed because "" is itself a case under test, so the zero value - // cannot double as "use the default". - ptr := func(s string) *string { return &s } - - for name, tc := range map[string]struct { - // cert builds the caller's certificate. Nil means a well-formed atelet - // on the node hosting the actor. - cert func(t *testing.T) *x509.Certificate - // noPeer calls the RPC with no transport credentials at all. - noPeer bool - - fixture actorFixture - - // Request fields override their defaults when non-nil. A nil worker - // override leaves the request pointing at the seeded worker. - worker *ateapipb.ObjectRef - noWorker bool - expectedActorUID *string - - wantCode codes.Code - }{ - "atelet on the hosting node mints for a running actor": { - fixture: runningOnNode(testNode), - wantCode: codes.OK, - }, - "actor is in ACTOR_STATE_DELETING with active worker assignment": { - fixture: actorFixture{state: ateapipb.ActorState_ACTOR_STATE_DELETING, workerNode: testNode}, - wantCode: codes.FailedPrecondition, - }, - "caller presented no certificate": { - noPeer: true, - fixture: runningOnNode(testNode), - wantCode: codes.Unauthenticated, - }, - "caller is not the atelet service account": { - cert: func(t *testing.T) *x509.Certificate { - id := podIdentityOn(testNode) - id.ServiceAccountName = "some-workload" - return newTestCert(t, path.Join("ns", ateletNamespace, "sa", "some-workload"), id) - }, - fixture: runningOnNode(testNode), - wantCode: codes.PermissionDenied, - }, - "caller is an atelet in the wrong namespace": { - cert: func(t *testing.T) *x509.Certificate { - id := podIdentityOn(testNode) - id.Namespace = "someone-elses-system" - return newTestCert(t, path.Join("ns", "someone-elses-system", "sa", ateletSA), id) - }, - fixture: runningOnNode(testNode), - wantCode: codes.PermissionDenied, - }, - "certificate carries no SPIFFE URI": { - cert: func(t *testing.T) *x509.Certificate { - return newTestCert(t, "", podIdentityOn(testNode)) - }, - fixture: runningOnNode(testNode), - wantCode: codes.PermissionDenied, - }, - "certificate carries no PodIdentity extension": { - cert: func(t *testing.T) *x509.Certificate { - return newTestCert(t, path.Join("ns", ateletNamespace, "sa", ateletSA), nil) - }, - fixture: runningOnNode(testNode), - wantCode: codes.PermissionDenied, - }, - "actor does not exist": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - assignedTo: resources.ActorRef{Atespace: testAtespace, Name: "no-such-actor"}, - }, - wantCode: codes.PermissionDenied, - }, - "actor exists under a different atespace": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - assignedTo: resources.ActorRef{Atespace: "some-other-atespace", Name: testActorName}, - }, - wantCode: codes.PermissionDenied, - }, - "actor is hosted on a different node": { - fixture: runningOnNode(testOtherNode), - wantCode: codes.PermissionDenied, - }, - "worker names a different Pod UID": { - fixture: runningOnNode(testNode), - worker: &ateapipb.ObjectRef{Name: "9a2f7b81-4c60-4d13-8e5a-3f0b6c8d1e27"}, - wantCode: codes.PermissionDenied, - }, - "worker is assigned to a different actor": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - assignedTo: resources.ActorRef{Atespace: testAtespace, Name: "someone-else"}, - }, - wantCode: codes.PermissionDenied, - }, - "worker is assigned to an actor with same name and atespace but different UID": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - mismatchedUID: true, - }, - wantCode: codes.PermissionDenied, - }, - "actor points to a different worker": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - actorWorkerName: "7c3d9e15-2a48-4b6f-9d01-8e5a3f0b6c8d", - }, - wantCode: codes.PermissionDenied, - }, - "hosting worker record is missing": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - noWorker: true, - }, - wantCode: codes.PermissionDenied, - }, - "actor has no placement": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - noPlacement: true, - }, - wantCode: codes.FailedPrecondition, - }, - "worker has been released": { - fixture: actorFixture{ - state: ateapipb.ActorState_ACTOR_STATE_RUNNING, - workerNode: testNode, - unassigned: true, - }, - wantCode: codes.PermissionDenied, - }, - "worker is unset": { - fixture: runningOnNode(testNode), - noWorker: true, - wantCode: codes.InvalidArgument, - }, - "worker name is empty": { - fixture: runningOnNode(testNode), - worker: &ateapipb.ObjectRef{}, - wantCode: codes.InvalidArgument, - }, - "worker carries an atespace": { - fixture: runningOnNode(testNode), - worker: &ateapipb.ObjectRef{Atespace: testAtespace, Name: testWorkerName}, - wantCode: codes.InvalidArgument, - }, - "expected actor UID is empty": { - fixture: runningOnNode(testNode), - expectedActorUID: ptr(""), - wantCode: codes.InvalidArgument, - }, - } { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - seedActor(t, ctx, st, tc.fixture) - srv := newTestServer(t, st) - - var callerCert *x509.Certificate - switch { - case tc.noPeer: - case tc.cert != nil: - callerCert = tc.cert(t) - default: - callerCert = ateletCertOn(t, testNode) - } - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatalf("read seeded actor: %v", err) - } - req := mintCertRequest(t, actor.GetMetadata().GetUid()) - switch { - case tc.noWorker: - req.Worker = nil - case tc.worker != nil: - req.Worker = tc.worker - } - if tc.expectedActorUID != nil { - req.ExpectedActorUid = *tc.expectedActorUID - } - resp, err := srv.MintCert(ctxWithCert(callerCert), req) - if got := status.Code(err); got != tc.wantCode { - t.Fatalf("MintCert() code = %v (err = %v), want %v", got, err, tc.wantCode) - } - if tc.wantCode == codes.PermissionDenied { - // Denials are deliberately indistinguishable (see denyMint): the - // message must not vary with why the mint was refused, or a - // caller could probe workers it is not entitled to. - msg := status.Convert(err).Message() - if msg != "caller is not permitted to mint actor credentials" && - msg != "caller is not permitted to mint credentials for this actor" { - t.Errorf("MintCert() denial leaks its reason: %q", msg) - } - } - if tc.wantCode != codes.OK { - if resp != nil { - t.Errorf("MintCert() returned a response alongside an error") - } - return - } - - if len(resp.GetActorCertificates()) == 0 { - t.Fatal("MintCert() returned no certificates") - } - leaf, err := x509.ParseCertificate(resp.GetActorCertificates()[0]) - if err != nil { - t.Fatalf("parse minted certificate: %v", err) - } - want := "spiffe://substrate-actor.local/atespace/" + testAtespace + "/actor/" + testActorName - if len(leaf.URIs) != 1 || leaf.URIs[0].String() != want { - t.Errorf("minted SPIFFE URI = %v, want %q", leaf.URIs, want) - } - }) - } -} - -func TestMintCertRejectsUnsupportedPurpose(t *testing.T) { - server := newTestServer(t, nil) - for name, purpose := range map[string]ateapipb.ActorCertificatePurpose{ - "unspecified": ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED, - "unknown": ateapipb.ActorCertificatePurpose(99), - } { - t.Run(name, func(t *testing.T) { - _, err := server.MintCert(ctxWithCert(ateletCertOn(t, testNode)), &ateapipb.MintCertRequest{Purpose: purpose}) - if got := status.Code(err); got != codes.InvalidArgument { - t.Fatalf("MintCert() code = %v (err = %v), want %v", got, err, codes.InvalidArgument) - } - }) - } -} - -// mintCertFor seeds a running actor and mints a certificate for it, returning -// the parsed leaf alongside the UID the store assigned the actor. The request -// is built from that UID, since it is only known once the actor exists. -func mintCertFor(t *testing.T, request func(actorUID string) *ateapipb.MintCertRequest) (*x509.Certificate, string, error) { - t.Helper() - - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - t.Cleanup(cleanup) - - seedActor(t, ctx, st, runningOnNode(testNode)) - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatalf("read seeded actor: %v", err) - } - actorUID := actor.GetMetadata().GetUid() - if actorUID == "" { - t.Fatal("seeded actor has no UID; the store is expected to assign one") - } - - resp, err := newTestServer(t, st).MintCert(ctxWithCert(ateletCertOn(t, testNode)), request(actorUID)) - if err != nil { - return nil, actorUID, err - } - if len(resp.GetActorCertificates()) == 0 { - t.Fatal("MintCert() returned no certificates") - } - leaf, err := x509.ParseCertificate(resp.GetActorCertificates()[0]) - if err != nil { - t.Fatalf("parse minted certificate: %v", err) - } - return leaf, actorUID, nil -} - -// TestMintCertEmbedsActorIdentity checks that a minted certificate carries the -// ActorIdentity extension, naming the actor the store knows about. -func TestMintCertEmbedsActorIdentity(t *testing.T) { - leaf, actorUID, err := mintCertFor(t, func(actorUID string) *ateapipb.MintCertRequest { - return mintCertRequest(t, actorUID) - }) - if err != nil { - t.Fatalf("MintCert(): %v", err) - } - - got, err := substratex509.ActorIdentityFromCertificate(leaf) - if err != nil { - t.Fatalf("ActorIdentityFromCertificate: %v", err) - } - if got == nil { - t.Fatal("minted certificate carries no ActorIdentity extension") - } - want := &substratex509.ActorIdentity{ - Atespace: testAtespace, - ActorName: testActorName, - ActorUid: actorUID, - Purpose: substratex509.ActorIdentityPurposeAtunnel, - } - if *got != *want { - t.Errorf("ActorIdentity = %+v, want %+v", got, want) - } -} - -// TestMintCertActorUID checks that expected_actor_uid rejects a request that -// crossed an actor reassignment. It never decides the certificate identity, -// which always comes from ateapi state. -func TestMintCertActorUID(t *testing.T) { - for name, tc := range map[string]struct { - requestUID func(actorUID string) string - wantCode codes.Code - }{ - "Matching": {requestUID: func(actorUID string) string { return actorUID }, wantCode: codes.OK}, - "Stale": {requestUID: func(string) string { return "9d1f7b06-3c58-4a2e-8b40-5f7c1e9a2d63" }, wantCode: codes.FailedPrecondition}, - } { - t.Run(name, func(t *testing.T) { - leaf, actorUID, err := mintCertFor(t, func(actorUID string) *ateapipb.MintCertRequest { - req := mintCertRequest(t, actorUID) - req.ExpectedActorUid = tc.requestUID(actorUID) - return req - }) - if got := status.Code(err); got != tc.wantCode { - t.Fatalf("MintCert() code = %v (err = %v), want %v", got, err, tc.wantCode) - } - if tc.wantCode != codes.OK { - return - } - - identity, err := substratex509.ActorIdentityFromCertificate(leaf) - if err != nil { - t.Fatalf("ActorIdentityFromCertificate: %v", err) - } - if identity == nil { - t.Fatal("minted certificate carries no ActorIdentity extension") - } - if identity.ActorUid != actorUID { - t.Errorf("ActorIdentity.ActorUid = %q, want the stored UID %q", identity.ActorUid, actorUID) - } - }) - } -} - -// TestMintCertActorState pins down that the actor's state does not gate -// minting: an actor still assigned to a worker on the caller's node gets a -// credential whatever state it carries, except while it is being deleted. -// -// ACTOR_STATE_RESUMING is the case that matters in practice. atelet mints -// while serving the Run/Restore RPC that ateapi issues before marking the -// actor RUNNING, so gating on RUNNING would make every resume unsatisfiable. -// -// The terminal states below are seeded with a worker assignment that the -// control plane would already have cleared, so they are not reachable in a -// healthy system; they are exercised to record that the assignment, not the -// state, is what the decision rests on. Enumerating the enum rather than -// listing states means a state added later is covered without editing this -// test. -func TestMintCertActorState(t *testing.T) { - for value, name := range ateapipb.ActorState_name { - actorState := ateapipb.ActorState(value) - wantCode := codes.OK - if actorState == ateapipb.ActorState_ACTOR_STATE_DELETING { - wantCode = codes.FailedPrecondition - } - t.Run(name, func(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - seedActor(t, ctx, st, actorFixture{state: actorState, workerNode: testNode}) - srv := newTestServer(t, st) - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatal(err) - } - _, err = srv.MintCert(ctxWithCert(ateletCertOn(t, testNode)), mintCertRequest(t, actor.GetMetadata().GetUid())) - if got := status.Code(err); got != wantCode { - t.Errorf("MintCert() code = %v (err = %v), want %v", got, err, wantCode) - } - }) - } -} - -// TestMintCertDeniesUnassignedActorWhateverItsState checks that the placement -// checks — not the state — are what stops a departed actor. A RUNNING actor -// whose worker has been released is refused just as a SUSPENDED one is. -func TestMintCertDeniesUnassignedActorWhateverItsState(t *testing.T) { - for name, actorState := range map[string]ateapipb.ActorState{ - "Running": ateapipb.ActorState_ACTOR_STATE_RUNNING, - "Suspended": ateapipb.ActorState_ACTOR_STATE_SUSPENDED, - } { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - // The worker still exists on the caller's node but has been released, - // which is what pause, suspend and crash all do before writing the - // terminal state. - seedActor(t, ctx, st, actorFixture{ - state: actorState, - workerNode: testNode, - unassigned: true, - }) - srv := newTestServer(t, st) - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatal(err) - } - _, err = srv.MintCert(ctxWithCert(ateletCertOn(t, testNode)), mintCertRequest(t, actor.GetMetadata().GetUid())) - if got := status.Code(err); got != codes.PermissionDenied { - t.Errorf("MintCert() code = %v (err = %v), want %v", got, err, codes.PermissionDenied) - } - }) - } -} - -// TestMintCertAuthorizesBeforeSigning checks that the gate runs before any CSR -// parsing or CA material is touched. An unauthorized caller must be rejected -// with PermissionDenied even when the rest of the request is unusable, so that -// a failure downstream of the gate can never mask the authorization decision. -func TestMintCertAuthorizesBeforeSigning(t *testing.T) { - ctx := context.Background() - st, cleanup := storetest.SetupTestStore(t) - defer cleanup() - - seedActor(t, ctx, st, runningOnNode(testOtherNode)) - - // A server whose CA pool file does not exist: reaching the signing path at - // all would surface as Internal rather than PermissionDenied. - workers := workercache.New(st, time.Hour) - cacheCtx, cancel := context.WithCancel(ctx) - defer cancel() - if err := workers.Start(cacheCtx); err != nil { - t.Fatal(err) - } - - ca, err := localca.GenerateCA("test-actor-ca", localca.KeyTypeED25519, 24*time.Hour) - if err != nil { - t.Fatalf("generate CA: %v", err) - } - pool := &localca.ConcretePool{ - CAs: []*localca.CA{ca}, - ActiveForSigning: "test-actor-ca", - } - - srv := New("issuer", "", pool, st, workers) - - actor, err := st.GetActor(ctx, resources.ActorRef{Atespace: testAtespace, Name: testActorName}) - if err != nil { - t.Fatal(err) - } - req := mintCertRequest(t, actor.GetMetadata().GetUid()) - req.CertificateSigningRequest = []byte("not a CSR") - _, err = srv.MintCert(ctxWithCert(ateletCertOn(t, testNode)), req) - if got := status.Code(err); got != codes.PermissionDenied { - t.Errorf("MintCert() code = %v (err = %v), want %v", got, err, codes.PermissionDenied) - } -} - -func TestValidateMintJWTRequest(t *testing.T) { - // This test verifies validation of user input for minting a JWT. - validReq := func(mods ...func(req *ateapipb.MintJWTRequest)) *ateapipb.MintJWTRequest { - req := &ateapipb.MintJWTRequest{ - Audience: []string{"aud1"}, - Atespace: "as1", - ActorName: "actor1", - ActorUid: "01234567-89ab-cdef-0123-456789abcdef", - } - for _, m := range mods { - m(req) - } - return req - } - - tests := []struct { - name string - req *ateapipb.MintJWTRequest - want field.ErrorList - }{{ - "valid", - validReq(), - nil, - }, { - "missing audience", - validReq(func(r *ateapipb.MintJWTRequest) { r.Audience = nil }), - field.ErrorList{field.Required(field.NewPath("audience"), "")}, - }, { - "too many audiences", - validReq(func(r *ateapipb.MintJWTRequest) { - r.Audience = make([]string, 17) - for i := range r.Audience { - r.Audience[i] = fmt.Sprintf("https://svc-%d.example.com", i) - } - }), - field.ErrorList{field.TooMany(field.NewPath("audience"), 17, 16).WithOrigin("maxItems")}, - }, { - "duplicate audience entry", - validReq(func(r *ateapipb.MintJWTRequest) { - r.Audience = []string{"https://a.example.com", "https://a.example.com"} - }), - field.ErrorList{field.Duplicate(field.NewPath("audience").Index(1), nil)}, - }, { - "audience entry too long", - validReq(func(r *ateapipb.MintJWTRequest) { r.Audience = []string{strings.Repeat("a", 513)} }), - field.ErrorList{field.TooLong(field.NewPath("audience").Index(0), nil, 512).WithOrigin("maxLength")}, - }, { - "missing atespace", - validReq(func(r *ateapipb.MintJWTRequest) { r.Atespace = "" }), - field.ErrorList{field.Required(field.NewPath("atespace"), "")}, - }, { - "invalid atespace", - validReq(func(r *ateapipb.MintJWTRequest) { r.Atespace = "AS1" }), - field.ErrorList{field.Invalid(field.NewPath("atespace"), nil, "").WithOrigin("format=k8s-short-name")}, - }, { - "missing actor_name", - validReq(func(r *ateapipb.MintJWTRequest) { r.ActorName = "" }), - field.ErrorList{field.Required(field.NewPath("actor_name"), "")}, - }, { - "invalid actor_name", - validReq(func(r *ateapipb.MintJWTRequest) { r.ActorName = "invalid value" }), - field.ErrorList{field.Invalid(field.NewPath("actor_name"), nil, "").WithOrigin("format=k8s-short-name")}, - }, { - "unspecified actor_uid", - validReq(func(r *ateapipb.MintJWTRequest) { r.ActorUid = "" }), - nil, - }, { - "invalid actor_uid", - validReq(func(r *ateapipb.MintJWTRequest) { r.ActorUid = "not a uid" }), - field.ErrorList{field.Invalid(field.NewPath("actor_uid"), nil, "").WithOrigin("format=k8s-uuid")}, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assertValidateErr(t, validateMintJWTRequest(context.Background(), tt.req), tt.want) - }) - } -} - -func TestValidateMintCertRequest(t *testing.T) { - // This test verifies validation of user input for minting a certificate. - validReq := func(mods ...func(req *ateapipb.MintCertRequest)) *ateapipb.MintCertRequest { - req := &ateapipb.MintCertRequest{ - Worker: &ateapipb.ObjectRef{Name: "worker1"}, - CertificateSigningRequest: []byte{0x01}, - ExpectedActorUid: "01234567-89ab-cdef-0123-456789abcdef", - Purpose: ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL, - } - for _, m := range mods { - m(req) - } - return req - } - - tests := []struct { - name string - req *ateapipb.MintCertRequest - want field.ErrorList - }{{ - "valid", - validReq(), - nil, - }, { - "oversized certificate_signing_request", - validReq(func(r *ateapipb.MintCertRequest) { r.CertificateSigningRequest = make([]byte, 16385) }), - field.ErrorList{field.TooLong(field.NewPath("certificate_signing_request"), nil, 16384)}, - }, { - "missing worker", - validReq(func(r *ateapipb.MintCertRequest) { r.Worker = nil }), - field.ErrorList{field.Required(field.NewPath("worker"), "")}, - }, { - "worker.atespace must be empty", - validReq(func(r *ateapipb.MintCertRequest) { r.Worker.Atespace = "as1" }), - field.ErrorList{field.Forbidden(field.NewPath("worker", "atespace"), "")}, - }, { - "missing worker.name", - validReq(func(r *ateapipb.MintCertRequest) { r.Worker.Name = "" }), - field.ErrorList{field.Required(field.NewPath("worker", "name"), "")}, - }, { - "invalid worker.name", - validReq(func(r *ateapipb.MintCertRequest) { r.Worker.Name = "invalid value" }), - field.ErrorList{field.Invalid(field.NewPath("worker", "name"), nil, "").WithOrigin("format=k8s-short-name")}, - }, { - "missing certificate_signing_request", - validReq(func(r *ateapipb.MintCertRequest) { r.CertificateSigningRequest = nil }), - field.ErrorList{field.Required(field.NewPath("certificate_signing_request"), "")}, - }, { - "missing expected_actor_uid", - validReq(func(r *ateapipb.MintCertRequest) { r.ExpectedActorUid = "" }), - field.ErrorList{field.Required(field.NewPath("expected_actor_uid"), "")}, - }, { - "invalid expected_actor_uid", - validReq(func(r *ateapipb.MintCertRequest) { r.ExpectedActorUid = "not a uid" }), - field.ErrorList{field.Invalid(field.NewPath("expected_actor_uid"), nil, "").WithOrigin("format=k8s-uuid")}, - }, { - "unspecified purpose", - validReq(func(r *ateapipb.MintCertRequest) { - r.Purpose = ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED - }), - field.ErrorList{field.Required(field.NewPath("purpose"), "")}, - }, { - "out-of-range purpose", - validReq(func(r *ateapipb.MintCertRequest) { r.Purpose = ateapipb.ActorCertificatePurpose(99) }), - field.ErrorList{field.Invalid(field.NewPath("purpose"), nil, "").WithOrigin("maximum")}, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assertValidateErr(t, validateMintCertRequest(context.Background(), tt.req), tt.want) - }) - } -} diff --git a/cmd/ateapi/internal/actoridentity/main_test.go b/cmd/ateapi/internal/actoridentity/main_test.go deleted file mode 100644 index b4ce4cec00..0000000000 --- a/cmd/ateapi/internal/actoridentity/main_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package actoridentity - -import ( - "testing" - - "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest" -) - -func TestMain(m *testing.M) { - storetest.RunTests(m) -} diff --git a/cmd/ateapi/internal/actoridjwt/actoridjwt.go b/cmd/ateapi/internal/actoridjwt/actoridjwt.go deleted file mode 100644 index 9e8b44b31b..0000000000 --- a/cmd/ateapi/internal/actoridjwt/actoridjwt.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package actoridjwt - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "encoding/base64" - "encoding/json" - "fmt" - "hash" - "time" -) - -type Claims struct { - // Claims from RFC7519 - Issuer string - Subject string - Audiences []string - Expiration time.Time - NotBefore time.Time - IssuedAt time.Time - JTI string - - // Claims from ADK's session model - Substrate SubstrateClaims -} - -type SubstrateClaims struct { - Atespace string - ActorName string - ActorUID string -} - -type wireHeader struct { - Type string `json:"typ,omitempty"` - Algorithm string `json:"alg,omitempty"` - KeyID string `json:"kid,omitempty"` -} - -type WireClaims struct { - // Claims from RFC7519 - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - Audiences json.RawMessage `json:"aud,omitempty"` - Expiration float64 `json:"exp,omitempty"` - NotBefore float64 `json:"nbf,omitempty"` - IssuedAt float64 `json:"iat,omitempty"` - JTI string `json:"jti,omitempty"` - - // Claims from ADK's session model. - Substrate WireSubstrateClaims `json:"ate.dev,omitempty"` -} - -type WireSubstrateClaims struct { - Atespace string `json:"atespace,omitempty"` - ActorName string `json:"actorName,omitempty"` - ActorUID string `json:"actorUID,omitempty"` -} - -func ClaimsToWire(claims *Claims) (*WireClaims, error) { - rawAudiences, err := json.Marshal(claims.Audiences) - if err != nil { - return nil, fmt.Errorf("while marshaling audience: %w", err) - } - - wire := &WireClaims{ - Issuer: claims.Issuer, - Subject: claims.Subject, - Audiences: rawAudiences, - Expiration: float64(claims.Expiration.Unix()), - NotBefore: float64(claims.NotBefore.Unix()), - IssuedAt: float64(claims.IssuedAt.Unix()), - JTI: claims.JTI, - Substrate: WireSubstrateClaims{ - Atespace: claims.Substrate.Atespace, - ActorName: claims.Substrate.ActorName, - ActorUID: claims.Substrate.ActorUID, - }, - } - - return wire, nil -} - -// Sign -func Sign(wireClaims *WireClaims, signingKey crypto.PrivateKey, algorithm, keyID string) (string, error) { - payloadBytes, err := json.Marshal(wireClaims) - if err != nil { - return "", fmt.Errorf("while marshaling payload: %w", err) - } - payloadB64 := base64.RawURLEncoding.EncodeToString(payloadBytes) - - rawHeader := wireHeader{ - Algorithm: algorithm, - KeyID: keyID, - } - headerBytes, err := json.Marshal(rawHeader) - if err != nil { - return "", fmt.Errorf("while marshaling header: %w", err) - } - headerB64 := base64.RawURLEncoding.EncodeToString(headerBytes) - - toBeSigned := headerB64 + "." + payloadB64 - - var sigBytes []byte - switch algorithm { - case "RS256": - rsaKey := signingKey.(*rsa.PrivateKey) - toBeSignedDigest := hashBytes(crypto.SHA256.New(), []byte(toBeSigned)) - sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, toBeSignedDigest) - if err != nil { - return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) - } - case "RS384": - rsaKey := signingKey.(*rsa.PrivateKey) - toBeSignedDigest := hashBytes(crypto.SHA384.New(), []byte(toBeSigned)) - sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA384, toBeSignedDigest) - if err != nil { - return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) - } - case "RS512": - rsaKey := signingKey.(*rsa.PrivateKey) - toBeSignedDigest := hashBytes(crypto.SHA512.New(), []byte(toBeSigned)) - sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA512, toBeSignedDigest) - if err != nil { - return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) - } - case "ES256": - // JOSE ES256 defined at https://datatracker.ietf.org/doc/rfc7518/ section 3.4 - ecdsaKey := signingKey.(*ecdsa.PrivateKey) - if ecdsaKey.Curve != elliptic.P256() { - return "", fmt.Errorf("ES256 requires a P256 key") - } - toBeSignedDigest := hashBytes(crypto.SHA256.New(), []byte(toBeSigned)) - r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, toBeSignedDigest) - if err != nil { - return "", fmt.Errorf("while performing ecdsa signature: %w", err) - } - sigBytes = make([]byte, 2*32) - r.FillBytes(sigBytes[:32]) - s.FillBytes(sigBytes[32:]) - default: - return "", fmt.Errorf("unimplemented algorithm %q", algorithm) - } - - sigB64 := base64.RawURLEncoding.EncodeToString(sigBytes) - - return toBeSigned + "." + sigB64, nil -} - -func hashBytes(hasher hash.Hash, bytes []byte) []byte { - hasher.Write(bytes) - hash := hasher.Sum(nil) - return hash[:] -} diff --git a/cmd/ateapi/internal/controlapi/actor.go b/cmd/ateapi/internal/controlapi/actor.go index ec12686c28..664767fad8 100644 --- a/cmd/ateapi/internal/controlapi/actor.go +++ b/cmd/ateapi/internal/controlapi/actor.go @@ -16,16 +16,26 @@ package controlapi import ( "context" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" "errors" "fmt" + "log/slog" + "net/url" + "path" "time" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" + "github.com/agent-substrate/substrate/internal/actoridjwt" "github.com/agent-substrate/substrate/internal/ateattr" "github.com/agent-substrate/substrate/internal/resources" + "github.com/agent-substrate/substrate/internal/substratex509" "github.com/agent-substrate/substrate/pkg/proto/ateapipb" "go.opentelemetry.io/otel/attribute" "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/peer" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "k8s.io/apimachinery/pkg/api/operation" @@ -462,3 +472,158 @@ func ValidateCustom_UpdateActorRequest_Actor(ctx context.Context, op operation.O errs = append(errs, validate.RequiredValue(ctx, op, fldPath.Child("metadata", "atespace"), &actor.Metadata.Atespace, nil)...) return errs } + +func (s *RPCService) MintActorJWT(ctx context.Context, req *ateapipb.MintActorJWTRequest) (*ateapipb.MintActorJWTResponse, error) { + // TODO(authz): Authorization layer needs to check whether the caller has + // the mintActorJWT permission/relation with this actor. This could be an + // atelet (via the relationship of the atelet running the actor), or the + // egress gateway (via a cluster-level grant?) + + // Verify that this actor exists in the store. It doesn't need to be + // running, since we may need to issue JWTs during actor boot / resume. + dbActor, err := s.impl.GetActor(ctx, resources.ActorRefFromObjectRef(req.GetActor())) + if errors.Is(err, store.ErrNotFound) { + return nil, status.Error(codes.NotFound, "actor not found") + } else if err != nil { + return nil, fmt.Errorf("while retrieving actor: %w", err) + } + if dbActor.GetMetadata().GetUid() != req.GetActorUid() { + return nil, status.Error(codes.Aborted, "conflict; actor has been deleted and recreated") + } + + // We only issue tokens with audience bindings. + if len(req.GetAudience()) == 0 { + return nil, fmt.Errorf("at least one audience must be requested") + } + + actorClaims := &actoridjwt.Claims{ + // TODO(identity): This needs to be configurable per-install. The user + // needs to make sure that the OIDC discovery docs are accessible at + // this URL, so that relying parties can verify the JWTs. + Issuer: "https://api.ate-system.svc", + // TODO(identity): this format is very likely going to change. + Subject: fmt.Sprintf("atespaces:%s:actors:%s", dbActor.GetMetadata().GetAtespace(), dbActor.GetMetadata().GetName()), + Audiences: req.GetAudience(), + Expiration: time.Now().Add(15 * time.Minute), + NotBefore: time.Now().Add(-5 * time.Minute), + IssuedAt: time.Now(), + JTI: rand.Text(), + + Substrate: actoridjwt.SubstrateClaims{ + Atespace: dbActor.GetMetadata().GetAtespace(), + ActorName: dbActor.GetMetadata().GetName(), + ActorUID: dbActor.GetMetadata().GetUid(), + }, + } + + actorJWT, err := s.actorIDJWTPool.SignJWT(actorClaims) + if err != nil { + return nil, fmt.Errorf("while signing actor JWT: %w", err) + } + + return &ateapipb.MintActorJWTResponse{ + ActorJwt: actorJWT, + }, nil +} + +func (s *RPCService) MintActorCertificate(ctx context.Context, req *ateapipb.MintActorCertificateRequest) (*ateapipb.MintActorCertificateResponse, error) { + // TODO(authz): Authorization layer needs to check whether the caller has + // the mintActorCertificate permission/relation with this actor. This + // could be an atelet (via the relationship of the atelet running the + // actor), or the egress gateway (via a cluster-level grant?) + + // Check that the caller authenticated with a client certificate --- we + // should not allow bootstrapping a proof-of-possession credential from a + // bearer credential. Note, we don't care that it was a certificate issued + // by Substrate, or something else. + // + // TODO(authz): Perhaps this can be handled with an OpenFGA condition. + p, ok := peer.FromContext(ctx) + if !ok { + return nil, status.Errorf(codes.Unauthenticated, "no peer transport information found") + } + tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo) + if !ok { + return nil, status.Errorf(codes.Unauthenticated, "unexpected peer transport credentials") + } + if len(tlsInfo.State.PeerCertificates) == 0 { + return nil, status.Errorf(codes.Unauthenticated, "could not verify peer certificate") + } + + // Verify that this actor exists in the store. It doesn't need to be + // running, since we may need to issue certificates during actor boot / resume. + dbActor, err := s.impl.GetActor(ctx, resources.ActorRefFromObjectRef(req.GetActor())) + if errors.Is(err, store.ErrNotFound) { + return nil, status.Error(codes.NotFound, "actor not found") + } else if err != nil { + return nil, fmt.Errorf("while retrieving actor: %w", err) + } + if dbActor.GetMetadata().GetUid() != req.GetActorUid() { + return nil, status.Error(codes.Aborted, "conflict; actor has been deleted and recreated") + } + + // Parse the CSR + csr, err := x509.ParseCertificateRequest(req.GetCertificateSigningRequest()) + if err != nil { + return nil, fmt.Errorf("while parsing CSR: %w", err) + } + if err := csr.CheckSignature(); err != nil { + slog.ErrorContext(ctx, "Failed to verify CSR signature", slog.Any("err", err)) + return nil, status.Errorf(codes.InvalidArgument, "Failed to verify CSR signature") + } + + // TODO(identity): Atunnel certificates should probably have a separate RPC, + // since different callers will be authorized to get atunnel certificates vs + // actor self-identity certificates. + var template *x509.Certificate + switch req.GetPurpose() { + case ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL: + template = &x509.Certificate{ + URIs: []*url.URL{ + { + Scheme: "spiffe", + // TODO(identity): Must be configurable per-install, so that each install can set it to a unique value. + Host: "substrate-actor.local", + // TODO(identity): Prefix with "atunnel" to prevent + // confusion between atunnel and an actor pretending to be + // an atunnel. + Path: path.Join("atespace", dbActor.GetMetadata().GetAtespace(), "actor", dbActor.GetMetadata().GetName()), + }, + }, + NotBefore: time.Now().Add(-5 * time.Minute), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: false, + Issuer: pkix.Name{ + CommonName: "api.ate-system.svc.cluster.local", + }, + } + default: + return nil, status.Errorf(codes.InvalidArgument, "certificate purpose must be specified") + } + + err = substratex509.AddActorIdentityToCertificate( + &substratex509.ActorIdentity{ + Atespace: dbActor.GetMetadata().GetAtespace(), + ActorName: dbActor.GetMetadata().GetName(), + ActorUid: dbActor.GetMetadata().GetUid(), + Purpose: substratex509.ActorIdentityPurposeAtunnel, + }, + template, + ) + if err != nil { + return nil, fmt.Errorf("while adding Substrate extension: %w", err) + } + + // Sign and return the actor cert. + chain, err := s.actorIDCAPool.CreateCertificate(template, csr.PublicKey) + if err != nil { + return nil, fmt.Errorf("while signing certificate: %w", err) + } + + return &ateapipb.MintActorCertificateResponse{ + ActorCertificates: chain, + }, nil +} diff --git a/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go b/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go index 1b1db08d9a..c29b758745 100644 --- a/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go +++ b/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go @@ -3145,3 +3145,31 @@ func TestCreateActor_RejectsUnknownRequestFields(t *testing.T) { _, err := tc.client.CreateActor(context.Background(), req) assertGrpcError(t, err, codes.InvalidArgument, "request: Invalid value: unknown field with protobuf tag 9999") } + +func TestMintActorJWT_Success(t *testing.T) { + ns := namespaceForTest("ns-mintactorjwt-success") + tc := setupTest(t, ns) + defer tc.cleanup() + + createResp, err := tc.client.CreateActor(t.Context(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: testAtespace, + Name: "id1", + }, + ActorTemplate: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl1"}, + WorkerSelector: &ateapipb.Selector{MatchLabels: map[string]string{"tier": "free"}}, + Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING}, + }}) + if err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + + _, err = tc.client.MintActorJWT(t.Context(), &ateapipb.MintActorJWTRequest{ + Actor: &ateapipb.ObjectRef{ + Atespace: createResp.GetMetadata().GetAtespace(), + Name: createResp.GetMetadata().GetName(), + }, + ActorUid: createResp.GetMetadata().GetUid(), + Audience: []string{"foo"}, + }) +} diff --git a/cmd/ateapi/internal/controlapi/functionaltest/common_test.go b/cmd/ateapi/internal/controlapi/functionaltest/common_test.go index 9a8f44bfa9..2183feab93 100644 --- a/cmd/ateapi/internal/controlapi/functionaltest/common_test.go +++ b/cmd/ateapi/internal/controlapi/functionaltest/common_test.go @@ -176,7 +176,22 @@ func setupTestWithVolumePlugins(t *testing.T, ns string, plugins map[string]volu mockDriverName: mockPlugin, } } - service := controlapi.NewRPCService(persistence, wc, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, scLister, dialer, instruments, "", volPlugins) + + service := controlapi.NewRPCService( + persistence, + wc, + workerPoolLister, + sandboxConfigLister, + csiDriverConfigLister, + scLister, + dialer, + instruments, + "", + volPlugins, + "", + nil, + nil, + ) // 5. Start REAL gRPC Server for ATE API grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor( diff --git a/cmd/ateapi/internal/controlapi/service.go b/cmd/ateapi/internal/controlapi/service.go index 842ac7dc81..03a87f90e2 100644 --- a/cmd/ateapi/internal/controlapi/service.go +++ b/cmd/ateapi/internal/controlapi/service.go @@ -20,6 +20,8 @@ import ( "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" "github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache" + "github.com/agent-substrate/substrate/internal/localca" + "github.com/agent-substrate/substrate/internal/localjwtauthority" "github.com/agent-substrate/substrate/internal/resources" "github.com/agent-substrate/substrate/internal/volume" "github.com/agent-substrate/substrate/internal/volume/csi" @@ -46,6 +48,10 @@ type RPCService struct { instruments *Instruments mu sync.RWMutex volumePlugins map[string]volume.VolumePluginControlPlane + + actorIdentityJWTIssuer string + actorIDJWTPool localjwtauthority.Pool + actorIDCAPool localca.Pool } var _ ateapipb.ControlServer = (*RPCService)(nil) @@ -70,17 +76,23 @@ func NewRPCService( instruments *Instruments, egressGatewayAddress string, volumePlugins map[string]volume.VolumePluginControlPlane, + actorIdentityJWTIssuer string, + actorIDJWTPool localjwtauthority.Pool, + actorIDCAPool localca.Pool, ) *RPCService { impl := newServiceImpl(persistence, storageClassLister) s := &RPCService{ - impl: impl, - persistence: persistence, - workerCache: workerCache, - workerPoolLister: workerPoolLister, - csiDriverConfigLister: csiDriverConfigLister, - dialer: dialer, - instruments: instruments, - volumePlugins: volumePlugins, + impl: impl, + persistence: persistence, + workerCache: workerCache, + workerPoolLister: workerPoolLister, + csiDriverConfigLister: csiDriverConfigLister, + dialer: dialer, + instruments: instruments, + volumePlugins: volumePlugins, + actorIdentityJWTIssuer: actorIdentityJWTIssuer, + actorIDJWTPool: actorIDJWTPool, + actorIDCAPool: actorIDCAPool, } s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, workerPoolLister, sandboxConfigLister, storageClassLister, instruments, egressGatewayAddress, s) s.workerWorkflow = NewWorkerWorkflow(impl) diff --git a/cmd/ateapi/internal/controlapi/validate.go b/cmd/ateapi/internal/controlapi/validate.go index 7a7765641d..04eec6ce9f 100644 --- a/cmd/ateapi/internal/controlapi/validate.go +++ b/cmd/ateapi/internal/controlapi/validate.go @@ -86,7 +86,7 @@ func ValidateCustom_WorkerAssignment_WorkerPodIp(_ context.Context, _ operation. // a guardrail, applied here because maxLength does not support bytes fields. const maxCSRBytes = 16384 -func ValidateCustom_MintCertRequest_CertificateSigningRequest(_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ []byte) field.ErrorList { +func ValidateCustom_MintActorCertificateRequest_CertificateSigningRequest(_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ []byte) field.ErrorList { if len(value) > maxCSRBytes { return field.ErrorList{field.TooLong(fldPath, nil, maxCSRBytes)} } diff --git a/cmd/ateapi/internal/controlapi/zz_generated.validation.go b/cmd/ateapi/internal/controlapi/zz_generated.validation.go index 825a603652..3a1dc87294 100644 --- a/cmd/ateapi/internal/controlapi/zz_generated.validation.go +++ b/cmd/ateapi/internal/controlapi/zz_generated.validation.go @@ -4325,13 +4325,13 @@ func Validate_LocalSnapshotInfo( return errs } -// Validate_MintCertRequest validates an instance of MintCertRequest according +// Validate_MintActorCertificateRequest validates an instance of MintActorCertificateRequest according // to declarative validation rules in the API schema. -func Validate_MintCertRequest( +func Validate_MintActorCertificateRequest( ctx context.Context, op operation.Operation, fldPath *field.Path, - obj, oldObj *ateapipb.MintCertRequest) (errs field.ErrorList) { + obj, oldObj *ateapipb.MintActorCertificateRequest) (errs field.ErrorList) { - { // field ateapipb.MintCertRequest.Worker + { // field ateapipb.MintActorCertificateRequest.Actor fn := func( fldPath *field.Path, obj, oldObj *ateapipb.ObjectRef, @@ -4351,102 +4351,83 @@ func Validate_MintCertRequest( if earlyReturn { return // do not proceed } - func() { // cohort = "atespace" - earlyReturn := false - if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", - func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.ForbiddenValue).MarkBeta().MarkShortCircuit(); len(e) != 0 { - errs = append(errs, e...) - earlyReturn = true - } - if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", - func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.OptionalValue).MarkBeta().MarkShortCircuit(); len(e) != 0 { - earlyReturn = true - } - if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", - func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.OptionalValue).MarkBeta().MarkShortCircuit(); len(e) != 0 { - earlyReturn = true - } - if earlyReturn { - return // do not proceed - } - }() // call the type's validation function errs = append(errs, Validate_ObjectRef(ctx, op, fldPath, obj, oldObj)...) return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintCertRequest) *ateapipb.ObjectRef { - return oldObj.Worker + func(oldObj *ateapipb.MintActorCertificateRequest) *ateapipb.ObjectRef { + return oldObj.Actor }) - errs = append(errs, fn(fldPath.Child("worker"), obj.Worker, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("actor"), obj.Actor, oldVal, oldObj != nil)...) } - { // field ateapipb.MintCertRequest.CertificateSigningRequest + { // field ateapipb.MintActorCertificateRequest.ActorUid fn := func( fldPath *field.Path, - obj, oldObj []byte, + obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { // don't revalidate unchanged data if oldValueCorrelated && op.Type == operation.Update { - if ateDeepEqual(obj, oldObj) { + if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { return nil } } // call field-attached validations earlyReturn := false - if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) earlyReturn = true } if earlyReturn { return // do not proceed } - // custom validation - if e := ValidateCustom_MintCertRequest_CertificateSigningRequest(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + if e := validate.UUID(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) } return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintCertRequest) []byte { - return oldObj.CertificateSigningRequest + func(oldObj *ateapipb.MintActorCertificateRequest) *string { + return &oldObj.ActorUid }) - errs = append(errs, fn(fldPath.Child("certificate_signing_request"), obj.CertificateSigningRequest, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("actor_uid"), &obj.ActorUid, oldVal, oldObj != nil)...) } - { // field ateapipb.MintCertRequest.ExpectedActorUid + { // field ateapipb.MintActorCertificateRequest.CertificateSigningRequest fn := func( fldPath *field.Path, - obj, oldObj *string, + obj, oldObj []byte, oldValueCorrelated bool) (errs field.ErrorList) { // don't revalidate unchanged data if oldValueCorrelated && op.Type == operation.Update { - if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + if ateDeepEqual(obj, oldObj) { return nil } } // call field-attached validations earlyReturn := false - if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) earlyReturn = true } if earlyReturn { return // do not proceed } - if e := validate.UUID(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + // custom validation + if e := ValidateCustom_MintActorCertificateRequest_CertificateSigningRequest(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) } return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintCertRequest) *string { - return &oldObj.ExpectedActorUid + func(oldObj *ateapipb.MintActorCertificateRequest) []byte { + return oldObj.CertificateSigningRequest }) - errs = append(errs, fn(fldPath.Child("expected_actor_uid"), &obj.ExpectedActorUid, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("certificate_signing_request"), obj.CertificateSigningRequest, oldVal, oldObj != nil)...) } - { // field ateapipb.MintCertRequest.Purpose + { // field ateapipb.MintActorCertificateRequest.Purpose fn := func( fldPath *field.Path, obj, oldObj *ateapipb.ActorCertificatePurpose, @@ -4475,7 +4456,7 @@ func Validate_MintCertRequest( return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintCertRequest) *ateapipb.ActorCertificatePurpose { + func(oldObj *ateapipb.MintActorCertificateRequest) *ateapipb.ActorCertificatePurpose { return &oldObj.Purpose }) errs = append(errs, fn(fldPath.Child("purpose"), &obj.Purpose, oldVal, oldObj != nil)...) @@ -4484,16 +4465,16 @@ func Validate_MintCertRequest( return errs } -// Validate_MintJWTRequest validates an instance of MintJWTRequest according +// Validate_MintActorJWTRequest validates an instance of MintActorJWTRequest according // to declarative validation rules in the API schema. -func Validate_MintJWTRequest( +func Validate_MintActorJWTRequest( ctx context.Context, op operation.Operation, fldPath *field.Path, - obj, oldObj *ateapipb.MintJWTRequest) (errs field.ErrorList) { + obj, oldObj *ateapipb.MintActorJWTRequest) (errs field.ErrorList) { - { // field ateapipb.MintJWTRequest.Audience + { // field ateapipb.MintActorJWTRequest.Actor fn := func( fldPath *field.Path, - obj, oldObj []string, + obj, oldObj *ateapipb.ObjectRef, oldValueCorrelated bool) (errs field.ErrorList) { // don't revalidate unchanged data if oldValueCorrelated && op.Type == operation.Update { @@ -4503,37 +4484,25 @@ func Validate_MintJWTRequest( } // call field-attached validations earlyReturn := false - if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16).MarkShortCircuit(); len(e) != 0 { - errs = append(errs, e...) - earlyReturn = true - } - if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) earlyReturn = true } if earlyReturn { return // do not proceed } - if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, - func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { - return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 512) - }); len(e) != 0 { - errs = append(errs, e...) - } - // lists with set semantics require unique values - if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual); len(e) != 0 { - errs = append(errs, e...) - } + // call the type's validation function + errs = append(errs, Validate_ObjectRef(ctx, op, fldPath, obj, oldObj)...) return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintJWTRequest) []string { - return oldObj.Audience + func(oldObj *ateapipb.MintActorJWTRequest) *ateapipb.ObjectRef { + return oldObj.Actor }) - errs = append(errs, fn(fldPath.Child("audience"), obj.Audience, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("actor"), obj.Actor, oldVal, oldObj != nil)...) } - { // field ateapipb.MintJWTRequest.Atespace + { // field ateapipb.MintActorJWTRequest.ActorUid fn := func( fldPath *field.Path, obj, oldObj *string, @@ -4553,79 +4522,59 @@ func Validate_MintJWTRequest( if earlyReturn { return // do not proceed } - if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + if e := validate.UUID(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) } return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintJWTRequest) *string { - return &oldObj.Atespace + func(oldObj *ateapipb.MintActorJWTRequest) *string { + return &oldObj.ActorUid }) - errs = append(errs, fn(fldPath.Child("atespace"), &obj.Atespace, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("actor_uid"), &obj.ActorUid, oldVal, oldObj != nil)...) } - { // field ateapipb.MintJWTRequest.ActorName + { // field ateapipb.MintActorJWTRequest.Audience fn := func( fldPath *field.Path, - obj, oldObj *string, + obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { // don't revalidate unchanged data if oldValueCorrelated && op.Type == operation.Update { - if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + if ateDeepEqual(obj, oldObj) { return nil } } // call field-attached validations earlyReturn := false - if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) earlyReturn = true } - if earlyReturn { - return // do not proceed - } - if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) - } - return - } - oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintJWTRequest) *string { - return &oldObj.ActorName - }) - errs = append(errs, fn(fldPath.Child("actor_name"), &obj.ActorName, oldVal, oldObj != nil)...) - } - - { // field ateapipb.MintJWTRequest.ActorUid - fn := func( - fldPath *field.Path, - obj, oldObj *string, - oldValueCorrelated bool) (errs field.ErrorList) { - // don't revalidate unchanged data - if oldValueCorrelated && op.Type == operation.Update { - if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { - return nil - } - } - // call field-attached validations - earlyReturn := false - if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { earlyReturn = true } if earlyReturn { return // do not proceed } - if e := validate.UUID(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, + func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { + return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 512) + }); len(e) != 0 { + errs = append(errs, e...) + } + // lists with set semantics require unique values + if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual); len(e) != 0 { errs = append(errs, e...) } return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.MintJWTRequest) *string { - return &oldObj.ActorUid + func(oldObj *ateapipb.MintActorJWTRequest) []string { + return oldObj.Audience }) - errs = append(errs, fn(fldPath.Child("actor_uid"), &obj.ActorUid, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("audience"), obj.Audience, oldVal, oldObj != nil)...) } return errs diff --git a/cmd/ateapi/main.go b/cmd/ateapi/main.go index a59fb3589f..7ce297531e 100644 --- a/cmd/ateapi/main.go +++ b/cmd/ateapi/main.go @@ -26,7 +26,6 @@ import ( "syscall" "time" - "github.com/agent-substrate/substrate/cmd/ateapi/internal/actoridentity" "github.com/agent-substrate/substrate/cmd/ateapi/internal/controlapi" "github.com/agent-substrate/substrate/cmd/ateapi/internal/oidcjwt" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" @@ -36,6 +35,7 @@ import ( "github.com/agent-substrate/substrate/internal/ateinterceptors" "github.com/agent-substrate/substrate/internal/credbundle" "github.com/agent-substrate/substrate/internal/localca" + "github.com/agent-substrate/substrate/internal/localjwtauthority" "github.com/agent-substrate/substrate/internal/serverboot" "github.com/agent-substrate/substrate/internal/version" "github.com/agent-substrate/substrate/internal/volume" @@ -185,18 +185,36 @@ func main() { volPlugins := make(map[string]volume.VolumePluginControlPlane) ateletDialer := controlapi.NewAteletDialer(workerPodInformer.GetIndexer(), ateletPodInformer.GetIndexer(), *ateletClientCredBundle, *podIdentityCACerts) - controlSrv := controlapi.NewRPCService(persistence, workerCache, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, storageClassLister, ateletDialer, instruments, *egressGatewayAddress, volPlugins) - - // Drive stored ActorTemplates through the golden actor flow. - templateReconciler := controlapi.NewActorTemplateReconciler(persistence, controlSrv, sandboxConfigLister) - templateReconciler.Start(shutdownCtx) actorIDCAPool, err := localca.NewRefreshingPool(*actorIDCAPoolFile) if err != nil { - serverboot.Fatal(ctx, "while loading the Actor ID CA", err) + serverboot.Fatal(ctx, "while loading the Actor ID certificate authority pool: %w", err) + } + + actorIDJWTAuthorityPool, err := localjwtauthority.NewRefreshingPool(*actorIDJWTPoolFile) + if err != nil { + serverboot.Fatal(ctx, "while loading the Actor ID JWT authority pool: %w", err) } - actorIdentitySrv := actoridentity.New(actorIdentityJWTIssuer, *actorIDJWTPoolFile, actorIDCAPool, persistence, workerCache) + controlSrv := controlapi.NewRPCService( + persistence, + workerCache, + workerPoolLister, + sandboxConfigLister, + csiDriverConfigLister, + storageClassLister, + ateletDialer, + instruments, + *egressGatewayAddress, + volPlugins, + actorIdentityJWTIssuer, + actorIDJWTAuthorityPool, + actorIDCAPool, + ) + + // Drive stored ActorTemplates through the golden actor flow. + templateReconciler := controlapi.NewActorTemplateReconciler(persistence, controlSrv, sandboxConfigLister) + templateReconciler.Start(shutdownCtx) lisCfg := &net.ListenConfig{} lis, err := lisCfg.Listen(ctx, "tcp", *listenAddr) @@ -230,7 +248,6 @@ func main() { ) reflection.Register(mux) ateapipb.RegisterControlServer(mux, controlSrv) - ateapipb.RegisterActorIdentityServer(mux, actorIdentitySrv) readiness := &serverboot.Readiness{} go serverboot.StartMetricsServer(ctx, serverboot.MetricsServerOptions{ diff --git a/cmd/atelet/credentialbroker.go b/cmd/atelet/credentialbroker.go index 97eabb10bd..85f1f72294 100644 --- a/cmd/atelet/credentialbroker.go +++ b/cmd/atelet/credentialbroker.go @@ -30,24 +30,26 @@ import ( type credentialBroker struct { ateletpb.UnimplementedCredentialBrokerServer - // actorIdentityClient resolves the authenticated worker's current assignment - // and signs its actor certificate. - actorIdentityClient ateapipb.ActorIdentityClient + controlClient ateapipb.ControlClient } func (b *credentialBroker) MintActorCertificate(ctx context.Context, req *ateletpb.MintActorCertificateRequest) (*ateletpb.MintActorCertificateResponse, error) { - // TODO: Before release, require the egress PEP to reject actor certificates - // whose ActorIdentity purpose is not atunnel. - // Worker identity comes only from the mTLS certificate. The expected actor - // UID is a stale-activation guard; ateapi derives the actor authoritatively. - workerIdentity, err := authenticatedWorkerIdentity(ctx) + // Check which ateom is calling. + _, err := authenticatedWorkerIdentity(ctx) if err != nil { return nil, err } - resp, err := b.actorIdentityClient.MintCert(ctx, &ateapipb.MintCertRequest{ - // Workers are global-scoped and named by their pod UID. - Worker: &ateapipb.ObjectRef{Name: workerIdentity.PodUID}, - ExpectedActorUid: req.GetExpectedActorUid(), + + // TODO(identity): Check that we believe that this ateom is running the + // requested actor? ate-api-server will further check that we (the atelet) + // are allowed to request a certificate for the actor. + + resp, err := b.controlClient.MintActorCertificate(ctx, &ateapipb.MintActorCertificateRequest{ + Actor: &ateapipb.ObjectRef{ + Atespace: req.GetActorAtespace(), + Name: req.GetActorName(), + }, + ActorUid: req.GetActorUid(), CertificateSigningRequest: req.GetCertificateSigningRequest(), Purpose: ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL, }) diff --git a/cmd/atelet/credentialbroker_test.go b/cmd/atelet/credentialbroker_test.go index 53b3b50302..fe73545a2f 100644 --- a/cmd/atelet/credentialbroker_test.go +++ b/cmd/atelet/credentialbroker_test.go @@ -24,45 +24,11 @@ import ( "testing" "time" - "github.com/agent-substrate/substrate/internal/proto/ateletpb" "github.com/agent-substrate/substrate/internal/substratex509" - "github.com/agent-substrate/substrate/pkg/proto/ateapipb" - "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" - "google.golang.org/protobuf/proto" ) -type brokerIdentityClient struct { - ateapipb.ActorIdentityClient - request *ateapipb.MintCertRequest -} - -func (c *brokerIdentityClient) MintCert(_ context.Context, req *ateapipb.MintCertRequest, _ ...grpc.CallOption) (*ateapipb.MintCertResponse, error) { - c.request = req - return &ateapipb.MintCertResponse{ActorCertificates: [][]byte{{1, 2, 3}}}, nil -} - -func TestCredentialBrokerForwardsAuthenticatedWorkerIdentity(t *testing.T) { - identity := &brokerIdentityClient{} - broker := &credentialBroker{actorIdentityClient: identity} - csr := []byte{4, 5, 6} - resp, err := broker.MintActorCertificate(workerContext(t, "worker-uid"), &ateletpb.MintActorCertificateRequest{ - CertificateSigningRequest: csr, - ExpectedActorUid: "actor-uid", - }) - if err != nil { - t.Fatal(err) - } - if !proto.Equal(resp, &ateletpb.MintActorCertificateResponse{ActorCertificates: [][]byte{{1, 2, 3}}}) { - t.Fatalf("response = %+v", resp) - } - want := &ateapipb.MintCertRequest{Worker: &ateapipb.ObjectRef{Name: "worker-uid"}, ExpectedActorUid: "actor-uid", CertificateSigningRequest: csr, Purpose: ateapipb.ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL} - if !proto.Equal(identity.request, want) { - t.Fatalf("MintCert request = %+v, want %+v", identity.request, want) - } -} - func workerContext(t *testing.T, podUID string) context.Context { t.Helper() cert := workerCertificate(t, podUID, "node") diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index 68afc5025b..66edb7df07 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -358,7 +358,7 @@ func main() { } brokerServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(brokerTLS))) ateletpb.RegisterCredentialBrokerServer(brokerServer, &credentialBroker{ - actorIdentityClient: ateapipb.NewActorIdentityClient(ateapiConn), + controlClient: ateapipb.NewControlClient(ateapiConn), }) go func() { if err := brokerServer.Serve(brokerLis); err != nil { diff --git a/cmd/ateom-gvisor/main.go b/cmd/ateom-gvisor/main.go index 388faeca89..abe5427658 100644 --- a/cmd/ateom-gvisor/main.go +++ b/cmd/ateom-gvisor/main.go @@ -1034,14 +1034,14 @@ func (s *AteomService) prepareActorEgress(ctx context.Context, actorUID string, SocketPath: ateompath.CredentialBrokerSocket, CredentialBundlePath: s.workerCredentialBundlePath, TrustBundlePath: s.podIdentityTrustBundlePath, - ExpectedActorUID: actorUID, + ActorUID: actorUID, }) if err != nil { return nil, fmt.Errorf("while configuring actor certificate broker: %w", err) } // Mint before starting the workload so configured tunneled egress fails // closed. The source retains the private key for mTLS and renewal. - expiresAt, err := certificateSource.Mint(ctx) + expiresAt, err := certificateSource.MintAteomCertificate(ctx) if err != nil { return nil, fmt.Errorf("while obtaining actor certificate: %w", err) } diff --git a/cmd/ateom-microvm/main.go b/cmd/ateom-microvm/main.go index 81a41a5bcf..6610be5feb 100644 --- a/cmd/ateom-microvm/main.go +++ b/cmd/ateom-microvm/main.go @@ -517,14 +517,15 @@ func (s *AteomService) prepareActorEgress(ctx context.Context, actorUID string, SocketPath: ateompath.CredentialBrokerSocket, CredentialBundlePath: s.workerCredentialBundlePath, TrustBundlePath: s.podIdentityTrustBundlePath, - ExpectedActorUID: actorUID, + + ActorUID: actorUID, }) if err != nil { return nil, fmt.Errorf("while configuring actor certificate broker: %w", err) } // Mint before starting the workload so configured tunneled egress fails // closed. The source retains the private key for mTLS and renewal. - expiresAt, err := certificateSource.Mint(ctx) + expiresAt, err := certificateSource.MintAteomCertificate(ctx) if err != nil { return nil, fmt.Errorf("while obtaining actor certificate: %w", err) } diff --git a/cmd/kubectl-ate/internal/cmd/admin_make_jwt_pool.go b/cmd/kubectl-ate/internal/cmd/admin_make_jwt_pool.go index 659c3ca37b..bba94fa095 100644 --- a/cmd/kubectl-ate/internal/cmd/admin_make_jwt_pool.go +++ b/cmd/kubectl-ate/internal/cmd/admin_make_jwt_pool.go @@ -48,8 +48,9 @@ var makeJwtPoolCmd = &cobra.Command{ return fmt.Errorf("while generating JWT authority: %w", err) } - pool := &localjwtauthority.Pool{ - Authorities: []*localjwtauthority.Authority{authority}, + pool := &localjwtauthority.ConcretePool{ + Authorities: []*localjwtauthority.Authority{authority}, + ActiveForSigning: keyID, } poolBytes, err := localjwtauthority.Marshal(pool) diff --git a/internal/actoridjwt/actoridjwt.go b/internal/actoridjwt/actoridjwt.go new file mode 100644 index 0000000000..4a40b95974 --- /dev/null +++ b/internal/actoridjwt/actoridjwt.go @@ -0,0 +1,85 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package actoridjwt + +import ( + "encoding/json" + "fmt" + "time" +) + +type Claims struct { + // Claims from RFC7519 + Issuer string + Subject string + Audiences []string + Expiration time.Time + NotBefore time.Time + IssuedAt time.Time + JTI string + + // Claims from ADK's session model + Substrate SubstrateClaims +} + +type SubstrateClaims struct { + Atespace string + ActorName string + ActorUID string +} + +type WireClaims struct { + // Claims from RFC7519 + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audiences json.RawMessage `json:"aud,omitempty"` + Expiration float64 `json:"exp,omitempty"` + NotBefore float64 `json:"nbf,omitempty"` + IssuedAt float64 `json:"iat,omitempty"` + JTI string `json:"jti,omitempty"` + + // Claims from ADK's session model. + Substrate WireSubstrateClaims `json:"ate.dev,omitempty"` +} + +type WireSubstrateClaims struct { + Atespace string `json:"atespace,omitempty"` + ActorName string `json:"actorName,omitempty"` + ActorUID string `json:"actorUID,omitempty"` +} + +func ClaimsToWire(claims *Claims) (*WireClaims, error) { + rawAudiences, err := json.Marshal(claims.Audiences) + if err != nil { + return nil, fmt.Errorf("while marshaling audience: %w", err) + } + + wire := &WireClaims{ + Issuer: claims.Issuer, + Subject: claims.Subject, + Audiences: rawAudiences, + Expiration: float64(claims.Expiration.Unix()), + NotBefore: float64(claims.NotBefore.Unix()), + IssuedAt: float64(claims.IssuedAt.Unix()), + JTI: claims.JTI, + Substrate: WireSubstrateClaims{ + Atespace: claims.Substrate.Atespace, + ActorName: claims.Substrate.ActorName, + ActorUID: claims.Substrate.ActorUID, + }, + } + + return wire, nil +} diff --git a/internal/atunnel/credential.go b/internal/atunnel/credential.go index 65090af769..44b235020c 100644 --- a/internal/atunnel/credential.go +++ b/internal/atunnel/credential.go @@ -40,10 +40,14 @@ import ( // BrokerCertificateSource owns atunnel's actor private key and obtains the // matching short-lived certificate from the node-local atelet. type BrokerCertificateSource struct { - socketPath string - expectedActorUID string - tlsConfig *tls.Config - privateKey *ecdsa.PrivateKey + socketPath string + + actorAtespace string + actorName string + actorUID string + + tlsConfig *tls.Config + privateKey *ecdsa.PrivateKey mu sync.RWMutex certificate *tls.Certificate @@ -58,18 +62,25 @@ type BrokerConfig struct { CredentialBundlePath string // TrustBundlePath verifies atelet's Pod certificate. TrustBundlePath string - // ExpectedActorUID prevents a mint started for an old activation from - // receiving the newly assigned actor's certificate. - ExpectedActorUID string + + // Which actor are we currently running? + ActorAtespace string + ActorName string + ActorUID string } // NewBrokerCertificateSource creates one actor key for this activation. The key // is reused across renewals and never leaves atunnel; only its CSR crosses the // credential broker socket. func NewBrokerCertificateSource(cfg BrokerConfig) (*BrokerCertificateSource, error) { - if cfg.SocketPath == "" || cfg.CredentialBundlePath == "" || cfg.TrustBundlePath == "" || cfg.ExpectedActorUID == "" { - return nil, fmt.Errorf("atunnel: credential broker socket, credentials, trust bundle, and expected actor UID are required") + if cfg.SocketPath == "" || cfg.CredentialBundlePath == "" || cfg.TrustBundlePath == "" { + return nil, fmt.Errorf("credential broker socket, credentials, trust bundle are required") + } + + if cfg.ActorAtespace == "" || cfg.ActorName == "" || cfg.ActorUID == "" { + return nil, fmt.Errorf("actor information is required") } + localCert, err := credbundle.Parse(cfg.CredentialBundlePath) if err != nil { return nil, fmt.Errorf("atunnel: load worker identity: %w", err) @@ -121,16 +132,27 @@ func NewBrokerCertificateSource(cfg BrokerConfig) (*BrokerCertificateSource, err }, } - return &BrokerCertificateSource{socketPath: cfg.SocketPath, expectedActorUID: cfg.ExpectedActorUID, tlsConfig: tlsConfig, privateKey: privateKey}, nil + return &BrokerCertificateSource{ + socketPath: cfg.SocketPath, + actorAtespace: cfg.ActorAtespace, + actorName: cfg.ActorName, + actorUID: cfg.ActorUID, + tlsConfig: tlsConfig, + privateKey: privateKey, + }, nil } -// Mint requests and installs a fresh certificate for the source's existing +// MintAteomCertificate requests and installs a fresh certificate for the source's existing // actor key. It returns the new expiry for renewal scheduling. -func (s *BrokerCertificateSource) Mint(ctx context.Context) (time.Time, error) { +func (s *BrokerCertificateSource) MintAteomCertificate(ctx context.Context) (time.Time, error) { csr, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{}, s.privateKey) if err != nil { return time.Time{}, fmt.Errorf("atunnel: create actor CSR: %w", err) } + + // TODO(identity): We should not be re-establishing a gRPC connection for + // every mint request. Do it once at atunnel startup. + // A fresh connection picks up rotated worker credentials and forces atelet's // current certificate and node identity to be verified for every mint. conn, err := grpc.NewClient("passthrough:///credential-broker", @@ -144,8 +166,10 @@ func (s *BrokerCertificateSource) Mint(ctx context.Context) (time.Time, error) { } defer conn.Close() resp, err := ateletpb.NewCredentialBrokerClient(conn).MintActorCertificate(ctx, &ateletpb.MintActorCertificateRequest{ + ActorAtespace: s.actorAtespace, + ActorName: s.actorName, + ActorUid: s.actorUID, CertificateSigningRequest: csr, - ExpectedActorUid: s.expectedActorUID, }) if err != nil { return time.Time{}, fmt.Errorf("atunnel: mint actor certificate: %w", err) @@ -175,7 +199,7 @@ func (s *BrokerCertificateSource) Mint(ctx context.Context) (time.Time, error) { if identity.Purpose != substratex509.ActorIdentityPurposeAtunnel { return time.Time{}, fmt.Errorf("atunnel: actor certificate is not scoped to atunnel") } - if identity.ActorUid != s.expectedActorUID { + if identity.ActorUid != s.actorUID { return time.Time{}, fmt.Errorf("atunnel: actor certificate is for an unexpected actor") } cert := &tls.Certificate{Certificate: chain, PrivateKey: s.privateKey, Leaf: leaf} diff --git a/internal/atunnel/credential_test.go b/internal/atunnel/credential_test.go index 057b6fe9e8..44ca64e8e1 100644 --- a/internal/atunnel/credential_test.go +++ b/internal/atunnel/credential_test.go @@ -43,7 +43,7 @@ func TestBrokerCertificateSourceMintsAndReusesKey(t *testing.T) { defer cancel() for range 2 { - if _, err := source.Mint(ctx); err != nil { + if _, err := source.MintAteomCertificate(ctx); err != nil { t.Fatal(err) } } @@ -68,7 +68,7 @@ func TestBrokerCertificateSourceRejectsAteletOnDifferentNode(t *testing.T) { source, _ := newTestBrokerCertificateSource(t, testAteletIdentity("node-b"), time.Hour) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if _, err := source.Mint(ctx); err == nil || !strings.Contains(err.Error(), "not on worker node") { + if _, err := source.MintAteomCertificate(ctx); err == nil || !strings.Contains(err.Error(), "not on worker node") { t.Fatalf("Mint() error = %v, want node identity rejection", err) } } @@ -77,7 +77,7 @@ func TestBrokerCertificateSourceRejectsExpiredCertificate(t *testing.T) { source, _ := newTestBrokerCertificateSource(t, testAteletIdentity("node-a"), -time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if _, err := source.Mint(ctx); err == nil || !strings.Contains(err.Error(), "invalid actor certificate lifetime") { + if _, err := source.MintAteomCertificate(ctx); err == nil || !strings.Contains(err.Error(), "invalid actor certificate lifetime") { t.Fatalf("Mint() error = %v, want expired certificate rejection", err) } } @@ -87,7 +87,7 @@ func TestBrokerCertificateSourceRejectsUnexpectedActor(t *testing.T) { broker.actorUID = "another-actor-uid" ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if _, err := source.Mint(ctx); err == nil || !strings.Contains(err.Error(), "unexpected actor") { + if _, err := source.MintAteomCertificate(ctx); err == nil || !strings.Contains(err.Error(), "unexpected actor") { t.Fatalf("Mint() error = %v, want actor UID rejection", err) } } @@ -101,7 +101,7 @@ type credentialBrokerStub struct { } func (s *credentialBrokerStub) MintActorCertificate(_ context.Context, req *ateletpb.MintActorCertificateRequest) (*ateletpb.MintActorCertificateResponse, error) { - if req.GetExpectedActorUid() != "actor-uid" { + if req.GetActorUid() != "actor-uid" { return nil, status.Error(codes.FailedPrecondition, "unexpected actor UID") } csr, err := x509.ParseCertificateRequest(req.GetCertificateSigningRequest()) @@ -183,7 +183,7 @@ func newTestBrokerCertificateSource(t *testing.T, ateletIdentity *substratex509. SocketPath: socketPath, CredentialBundlePath: credentialPath, TrustBundlePath: trustPath, - ExpectedActorUID: "actor-uid", + ActorUID: "actor-uid", }) if err != nil { t.Fatal(err) diff --git a/internal/atunnel/egress.go b/internal/atunnel/egress.go index c42899c019..dbd317b7fc 100644 --- a/internal/atunnel/egress.go +++ b/internal/atunnel/egress.go @@ -35,7 +35,7 @@ type egressDialer interface { } type actorCertificateSource interface { - Mint(context.Context) (time.Time, error) + MintAteomCertificate(context.Context) (time.Time, error) } // OriginalDestination returns the address that a transparently intercepted @@ -142,7 +142,7 @@ func (e *Egress) renew(active *egressActivation, expiresAt time.Time) { slog.Time("expiredAt", expiresAt)) expired = true } - nextExpiry, err := active.certificateSource.Mint(active.ctx) + nextExpiry, err := active.certificateSource.MintAteomCertificate(active.ctx) if err != nil { code := status.Code(err) if code == codes.FailedPrecondition || code == codes.PermissionDenied { diff --git a/internal/atunnel/egress_test.go b/internal/atunnel/egress_test.go index 18440ec5f2..4463dd5548 100644 --- a/internal/atunnel/egress_test.go +++ b/internal/atunnel/egress_test.go @@ -353,7 +353,7 @@ type fakeActorCertificateSource struct { release <-chan struct{} } -func (s fakeActorCertificateSource) Mint(context.Context) (time.Time, error) { +func (s fakeActorCertificateSource) MintAteomCertificate(context.Context) (time.Time, error) { if s.calls != nil { s.calls.Add(1) } diff --git a/internal/localca/localca.go b/internal/localca/localca.go index 760411743c..3b6272ee97 100644 --- a/internal/localca/localca.go +++ b/internal/localca/localca.go @@ -44,8 +44,6 @@ import ( "os" "sync" "time" - - "k8s.io/utils/clock" ) // Pool is the interface for a CA pool. @@ -82,7 +80,6 @@ type Pool interface { // components to restart. type RefreshingPool struct { stateFile string - clock clock.PassiveClock // lock covers nextLoad and pool lock sync.Mutex @@ -95,8 +92,9 @@ var _ Pool = (*RefreshingPool)(nil) func NewRefreshingPool(stateFile string) (*RefreshingPool, error) { rp := &RefreshingPool{ stateFile: stateFile, - clock: clock.RealClock{}, } + rp.lock.Lock() + defer rp.lock.Unlock() if err := rp.refreshIfNecessary(); err != nil { return nil, fmt.Errorf("while loading pool: %w", err) } @@ -105,7 +103,7 @@ func NewRefreshingPool(stateFile string) (*RefreshingPool, error) { // refreshIfNecessary must be called while p.lock is held. func (p *RefreshingPool) refreshIfNecessary() error { - if p.pool != nil && p.clock.Now().Before(p.nextLoad) { + if p.pool != nil && time.Now().Before(p.nextLoad) { return nil } @@ -120,7 +118,7 @@ func (p *RefreshingPool) refreshIfNecessary() error { } p.pool = pool - p.nextLoad = p.clock.Now().Add(time.Minute) + p.nextLoad = time.Now().Add(time.Minute) return nil } diff --git a/internal/localca/localca_test.go b/internal/localca/localca_test.go index 67665e2173..3dd1aed99d 100644 --- a/internal/localca/localca_test.go +++ b/internal/localca/localca_test.go @@ -164,7 +164,7 @@ func TestRefreshingPool(t *testing.T) { t.Fatalf("Unexpected error marshaling pool 1: %v", err) } - ca2, err := GenerateCA("1", KeyTypeED25519, 365*24*time.Hour) + ca2, err := GenerateCA("2", KeyTypeED25519, 365*24*time.Hour) if err != nil { t.Fatalf("Unexpected error generating CA 2: %v", err) } diff --git a/internal/localjwtauthority/localjwtauthority.go b/internal/localjwtauthority/localjwtauthority.go index 97021fb2dc..8a3d2a9332 100644 --- a/internal/localjwtauthority/localjwtauthority.go +++ b/internal/localjwtauthority/localjwtauthority.go @@ -20,37 +20,266 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/rsa" "crypto/x509" + "encoding/base64" "encoding/json" - "encoding/pem" "fmt" + "hash" + "os" + "sync" + "time" + + "github.com/agent-substrate/substrate/internal/actoridjwt" ) -type Pool struct { +// Pool is the interface for a JWT signing pool. +// +// Logically, a Pool is a collection of multiple authorities. One or more are +// designated as active for signing. The rest are inactive, but are still +// trusted for verifying JWTs. +// +// The active/inactive desngination allows a Pool to be seamlessly rotated. +// +// Normally, we let callers define their own compatibility interfaces. But in +// most cases you'll want to either use a RefreshingPool (for controllers and +// servers), or a ConcretePool (for CLIs and tests). +type Pool interface { + // SignJWT signs a JWT with the given claims. + SignJWT(*actoridjwt.Claims) (string, error) + + // VerificationKeys returns the verification key set of this pool, for + // exporting via OpenID Connect Discovery. + VerificationKeys() ([]*VerificationKey, error) +} + +type VerificationKey struct { + KeyID string + PublicKey crypto.PublicKey +} + +// RefreshingPool is a wrapper around ConcretePool that periodically reloads the +// state from disk. This allows JWT signing and verification to continue +// working seamlessly even as an administrator rotates the pool, without +// requiring any components to restart. +type RefreshingPool struct { + stateFile string + + // lock covers nextLoad and pool + lock sync.Mutex + nextLoad time.Time + pool *ConcretePool +} + +var _ Pool = (*RefreshingPool)(nil) + +func NewRefreshingPool(stateFile string) (*RefreshingPool, error) { + rp := &RefreshingPool{ + stateFile: stateFile, + } + rp.lock.Lock() + defer rp.lock.Unlock() + if err := rp.refreshIfNecessary(); err != nil { + return nil, fmt.Errorf("while loading pool: %w", err) + } + return rp, nil +} + +// refreshIfNecessary must be called under p.lock +func (p *RefreshingPool) refreshIfNecessary() error { + if p.pool != nil && time.Now().Before(p.nextLoad) { + return nil + } + + poolBytes, err := os.ReadFile(p.stateFile) + if err != nil { + return fmt.Errorf("while reading pool state: %w", err) + } + + pool, err := Unmarshal(poolBytes) + if err != nil { + return fmt.Errorf("while unmarshaling pool: %w", err) + } + + p.pool = pool + p.nextLoad = time.Now().Add(time.Minute) + + return nil +} + +func (p *RefreshingPool) SignJWT(claims *actoridjwt.Claims) (string, error) { + p.lock.Lock() + defer p.lock.Unlock() + if err := p.refreshIfNecessary(); err != nil { + return "", fmt.Errorf("while refreshing pool: %w", err) + } + return p.pool.SignJWT(claims) +} + +func (p *RefreshingPool) VerificationKeys() ([]*VerificationKey, error) { + p.lock.Lock() + defer p.lock.Unlock() + if err := p.refreshIfNecessary(); err != nil { + return nil, fmt.Errorf("while refreshing pool: %w", err) + } + return p.pool.VerificationKeys() +} + +type ConcretePool struct { Authorities []*Authority + // Which authority is active for signing? + ActiveForSigning string +} + +var _ Pool = (*ConcretePool)(nil) + +func (p *ConcretePool) SignJWT(claims *actoridjwt.Claims) (string, error) { + wireClaims, err := actoridjwt.ClaimsToWire(claims) + if err != nil { + return "", fmt.Errorf("while converting claims to wire model: %w", err) + } + + payloadBytes, err := json.Marshal(wireClaims) + if err != nil { + return "", fmt.Errorf("while marshaling payload: %w", err) + } + + // TODO(ahmedtd): Select authority + var selectedAuthority *Authority + if p.ActiveForSigning != "" { + for _, authority := range p.Authorities { + if authority.ID == p.ActiveForSigning { + selectedAuthority = authority + } + } + if selectedAuthority == nil { + return "", fmt.Errorf("selected authority %q not present", p.ActiveForSigning) + } + } else { + // Fall back to first entry. + if len(p.Authorities) == 0 { + return "", fmt.Errorf("pool has no authorities defined") + } + selectedAuthority = p.Authorities[0] + } + + // TODO(identity): The key IDs should probably be SHA256 of the key, to + // prevent user misuse. + jwt, err := sign(payloadBytes, selectedAuthority.SigningKey, selectedAuthority.Algorithm, selectedAuthority.ID) + if err != nil { + return "", fmt.Errorf("while signing JWT: %w", err) + } + + return jwt, nil +} + +func (p *ConcretePool) VerificationKeys() ([]*VerificationKey, error) { + var keys []*VerificationKey + for _, authority := range p.Authorities { + vk := &VerificationKey{ + KeyID: authority.ID, + PublicKey: authority.SigningKey.Public(), + } + keys = append(keys, vk) + } + return keys, nil +} + +type wireHeader struct { + Type string `json:"typ,omitempty"` + Algorithm string `json:"alg,omitempty"` + KeyID string `json:"kid,omitempty"` +} + +func sign(payloadBytes []byte, signingKey crypto.PrivateKey, algorithm, keyID string) (string, error) { + payloadB64 := base64.RawURLEncoding.EncodeToString(payloadBytes) + + rawHeader := wireHeader{ + Algorithm: algorithm, + KeyID: keyID, + } + headerBytes, err := json.Marshal(rawHeader) + if err != nil { + return "", fmt.Errorf("while marshaling header: %w", err) + } + headerB64 := base64.RawURLEncoding.EncodeToString(headerBytes) + + toBeSigned := headerB64 + "." + payloadB64 + + var sigBytes []byte + switch algorithm { + case "RS256": + rsaKey := signingKey.(*rsa.PrivateKey) + toBeSignedDigest := hashBytes(crypto.SHA256.New(), []byte(toBeSigned)) + sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, toBeSignedDigest) + if err != nil { + return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) + } + case "RS384": + rsaKey := signingKey.(*rsa.PrivateKey) + toBeSignedDigest := hashBytes(crypto.SHA384.New(), []byte(toBeSigned)) + sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA384, toBeSignedDigest) + if err != nil { + return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) + } + case "RS512": + rsaKey := signingKey.(*rsa.PrivateKey) + toBeSignedDigest := hashBytes(crypto.SHA512.New(), []byte(toBeSigned)) + sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA512, toBeSignedDigest) + if err != nil { + return "", fmt.Errorf("while performing RSA PKCS1v15 signature: %w", err) + } + case "ES256": + // JOSE ES256 defined at https://datatracker.ietf.org/doc/rfc7518/ section 3.4 + ecdsaKey := signingKey.(*ecdsa.PrivateKey) + if ecdsaKey.Curve != elliptic.P256() { + return "", fmt.Errorf("ES256 requires a P256 key") + } + toBeSignedDigest := hashBytes(crypto.SHA256.New(), []byte(toBeSigned)) + r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, toBeSignedDigest) + if err != nil { + return "", fmt.Errorf("while performing ecdsa signature: %w", err) + } + sigBytes = make([]byte, 2*32) + r.FillBytes(sigBytes[:32]) + s.FillBytes(sigBytes[32:]) + default: + return "", fmt.Errorf("unimplemented algorithm %q", algorithm) + } + + sigB64 := base64.RawURLEncoding.EncodeToString(sigBytes) + + return toBeSigned + "." + sigB64, nil +} + +func hashBytes(hasher hash.Hash, bytes []byte) []byte { + hasher.Write(bytes) + hash := hasher.Sum(nil) + return hash[:] } type Authority struct { ID string Algorithm string - SigningKey crypto.PrivateKey + SigningKey crypto.Signer } type serializedPool struct { - Authorities []*serializedAuthority + Authorities []*serializedAuthority + ActiveForSigning string } type serializedAuthority struct { ID string Algorithm string SigningKeyPKCS8 []byte - SigningKeyPEM string } // Marshal serializes a Pool to JSON. -func Marshal(pool *Pool) ([]byte, error) { +func Marshal(pool *ConcretePool) ([]byte, error) { wire := &serializedPool{} + wire.ActiveForSigning = pool.ActiveForSigning for _, authority := range pool.Authorities { authorityWire := &serializedAuthority{} authorityWire.ID = authority.ID @@ -74,25 +303,29 @@ func Marshal(pool *Pool) ([]byte, error) { } // Unmarshal loads a Pool from JSON. -func Unmarshal(wireBytes []byte) (*Pool, error) { +func Unmarshal(wireBytes []byte) (*ConcretePool, error) { wire := &serializedPool{} if err := json.Unmarshal(wireBytes, wire); err != nil { return nil, fmt.Errorf("while unmarshaling JSON: %w", err) } - pool := &Pool{} + pool := &ConcretePool{ + ActiveForSigning: wire.ActiveForSigning, + } for _, wireAuthority := range wire.Authorities { authority := &Authority{ ID: wireAuthority.ID, Algorithm: wireAuthority.Algorithm, } - signingKey, err := parsePrivateKey(wireAuthority.SigningKeyPKCS8, wireAuthority.SigningKeyPEM) + key, err := x509.ParsePKCS8PrivateKey(wireAuthority.SigningKeyPKCS8) if err != nil { return nil, fmt.Errorf("while parsing signing key: %w", err) } - authority.SigningKey = signingKey + + // All key types from ParsePKCS8PrivateKey implement Signer + authority.SigningKey = key.(crypto.Signer) pool.Authorities = append(pool.Authorities, authority) } @@ -100,28 +333,6 @@ func Unmarshal(wireBytes []byte) (*Pool, error) { return pool, nil } -func parsePrivateKey(pkcs8 []byte, pemData string) (crypto.PrivateKey, error) { - if len(pkcs8) != 0 { - return x509.ParsePKCS8PrivateKey(pkcs8) - } - - block, _ := pem.Decode([]byte(pemData)) - if block == nil { - return nil, fmt.Errorf("missing PEM block") - } - - if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil { - return key, nil - } - if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil { - return key, nil - } - if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil { - return key, nil - } - return nil, fmt.Errorf("unsupported private key PEM type %q", block.Type) -} - // GenerateECDSAP256Authority generates an ECDSA P256 JWT signing key. func GenerateECDSAP256Authority(id string) (*Authority, error) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/internal/localjwtauthority/localjwtauthority_test.go b/internal/localjwtauthority/localjwtauthority_test.go index 7f25a72ea6..a3b2e02ce7 100644 --- a/internal/localjwtauthority/localjwtauthority_test.go +++ b/internal/localjwtauthority/localjwtauthority_test.go @@ -15,48 +15,83 @@ package localjwtauthority import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/json" - "encoding/pem" + "os" + "path/filepath" "testing" + "testing/synctest" + "time" + + "github.com/google/go-cmp/cmp" ) -func TestUnmarshalPEMSigningKey(t *testing.T) { - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +func TestRefreshingPool(t *testing.T) { + ca1, err := GenerateECDSAP256Authority("1") if err != nil { - t.Fatalf("GenerateKey(): %v", err) + t.Fatalf("Unexpected error generating CA 1: %v", err) } - keyDER, err := x509.MarshalECPrivateKey(key) - if err != nil { - t.Fatalf("MarshalECPrivateKey(): %v", err) + pool1 := &ConcretePool{ + Authorities: []*Authority{ca1}, + ActiveForSigning: "1", } - keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})) - - data, err := json.Marshal(&serializedPool{ - Authorities: []*serializedAuthority{{ - ID: "1", - Algorithm: "ES256", - SigningKeyPEM: keyPEM, - }}, - }) + pool1Bytes, err := Marshal(pool1) if err != nil { - t.Fatalf("Marshal(): %v", err) + t.Fatalf("Unexpected error marshaling pool 1: %v", err) } - pool, err := Unmarshal(data) + ca2, err := GenerateECDSAP256Authority("2") if err != nil { - t.Fatalf("Unmarshal(): %v", err) + t.Fatalf("Unexpected error generating CA 2: %v", err) } - if len(pool.Authorities) != 1 { - t.Fatalf("Authorities length = %d, want 1", len(pool.Authorities)) + pool2 := &ConcretePool{ + Authorities: []*Authority{ca2}, + ActiveForSigning: "2", } - if pool.Authorities[0].Algorithm != "ES256" { - t.Fatalf("Algorithm = %q, want ES256", pool.Authorities[0].Algorithm) - } - if _, ok := pool.Authorities[0].SigningKey.(*ecdsa.PrivateKey); !ok { - t.Fatalf("SigningKey type = %T, want *ecdsa.PrivateKey", pool.Authorities[0].SigningKey) + pool2Bytes, err := Marshal(pool2) + if err != nil { + t.Fatalf("Unexpected error marshaling pool 2: %v", err) } + + synctest.Test(t, func(t *testing.T) { + tempDir := t.TempDir() + poolFile := filepath.Join(tempDir, "pool.json") + + if err := os.WriteFile(poolFile, pool1Bytes, 0o600); err != nil { + t.Fatalf("Unexpected error writing pool 1: %v", err) + } + + refreshingPool, err := NewRefreshingPool(poolFile) + if err != nil { + t.Fatalf("Unexpected error creating refreshing pool: %v", err) + } + + gotVerificationKeys, err := refreshingPool.VerificationKeys() + if err != nil { + t.Fatalf("Unexpected errors getting anchors from refreshing pool: %v", err) + } + wantVerificationKeys, err := pool1.VerificationKeys() + if err != nil { + t.Fatalf("Unexpected errors getting anchors from pool 1: %v", err) + } + if diff := cmp.Diff(gotVerificationKeys, wantVerificationKeys); diff != "" { + t.Fatalf("Refreshing pool returned wrong trust anchors; diff (-got +want)\n%s", diff) + } + + // Write pool2 and advance past the cache threshold. + if err := os.WriteFile(poolFile, pool2Bytes, 0o600); err != nil { + t.Fatalf("Unexpected error writing pool 2: %v", err) + } + time.Sleep(61 * time.Second) + + gotVerificationKeys, err = refreshingPool.VerificationKeys() + if err != nil { + t.Fatalf("Unexpected errors getting anchors from refreshing pool: %v", err) + } + wantVerificationKeys, err = pool2.VerificationKeys() + if err != nil { + t.Fatalf("Unexpected errors getting anchors from pool 2: %v", err) + } + if diff := cmp.Diff(gotVerificationKeys, wantVerificationKeys); diff != "" { + t.Fatalf("Refreshing pool returned wrong trust anchors after file update; diff (-got +want)\n%s", diff) + } + }) } diff --git a/internal/proto/ateletpb/atelet.pb.go b/internal/proto/ateletpb/atelet.pb.go index a060b537cd..ff90978ba4 100644 --- a/internal/proto/ateletpb/atelet.pb.go +++ b/internal/proto/ateletpb/atelet.pb.go @@ -206,14 +206,17 @@ func (SnapshotScope) EnumDescriptor() ([]byte, []int) { type MintActorCertificateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` + // The actor for which the certificate should be issued. + ActorAtespace string `protobuf:"bytes,3,opt,name=actor_atespace,json=actorAtespace,proto3" json:"actor_atespace,omitempty"` + ActorName string `protobuf:"bytes,4,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + ActorUid string `protobuf:"bytes,5,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` // DER-encoded PKCS #10 certificate signing request. Atunnel retains the // corresponding private key. CertificateSigningRequest []byte `protobuf:"bytes,1,opt,name=certificate_signing_request,json=certificateSigningRequest,proto3" json:"certificate_signing_request,omitempty"` - // Actor incarnation this activation expects. Ateapi resolves the actor from - // the authenticated worker and rejects the request if its UID differs. - ExpectedActorUid string `protobuf:"bytes,2,opt,name=expected_actor_uid,json=expectedActorUid,proto3" json:"expected_actor_uid,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *MintActorCertificateRequest) Reset() { @@ -246,20 +249,34 @@ func (*MintActorCertificateRequest) Descriptor() ([]byte, []int) { return file_atelet_proto_rawDescGZIP(), []int{0} } -func (x *MintActorCertificateRequest) GetCertificateSigningRequest() []byte { +func (x *MintActorCertificateRequest) GetActorAtespace() string { if x != nil { - return x.CertificateSigningRequest + return x.ActorAtespace } - return nil + return "" } -func (x *MintActorCertificateRequest) GetExpectedActorUid() string { +func (x *MintActorCertificateRequest) GetActorName() string { if x != nil { - return x.ExpectedActorUid + return x.ActorName } return "" } +func (x *MintActorCertificateRequest) GetActorUid() string { + if x != nil { + return x.ActorUid + } + return "" +} + +func (x *MintActorCertificateRequest) GetCertificateSigningRequest() []byte { + if x != nil { + return x.CertificateSigningRequest + } + return nil +} + type MintActorCertificateResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // DER-encoded leaf followed by any intermediate certificates. @@ -2578,10 +2595,13 @@ var File_atelet_proto protoreflect.FileDescriptor const file_atelet_proto_rawDesc = "" + "\n" + - "\fatelet.proto\x12\x06atelet\"\x8b\x01\n" + - "\x1bMintActorCertificateRequest\x12>\n" + - "\x1bcertificate_signing_request\x18\x01 \x01(\fR\x19certificateSigningRequest\x12,\n" + - "\x12expected_actor_uid\x18\x02 \x01(\tR\x10expectedActorUid\"M\n" + + "\fatelet.proto\x12\x06atelet\"\xc0\x01\n" + + "\x1bMintActorCertificateRequest\x12%\n" + + "\x0eactor_atespace\x18\x03 \x01(\tR\ractorAtespace\x12\x1d\n" + + "\n" + + "actor_name\x18\x04 \x01(\tR\tactorName\x12\x1b\n" + + "\tactor_uid\x18\x05 \x01(\tR\bactorUid\x12>\n" + + "\x1bcertificate_signing_request\x18\x01 \x01(\fR\x19certificateSigningRequest\"M\n" + "\x1cMintActorCertificateResponse\x12-\n" + "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates\"\xa6\x02\n" + "\x10TerminateRequest\x12(\n" + diff --git a/internal/proto/ateletpb/atelet.proto b/internal/proto/ateletpb/atelet.proto index aa3fbbcf2a..b65e8eea9d 100644 --- a/internal/proto/ateletpb/atelet.proto +++ b/internal/proto/ateletpb/atelet.proto @@ -18,19 +18,30 @@ package atelet; option go_package = "github.com/agent-substrate/substrate/internal/proto/ateletpb"; -// CredentialBroker gives an authenticated worker its current actor credential. +// CredentialBroker provides on-demand services to ateom. +// +// TODO(identity): Rename to something more generic like AteomSupportService. service CredentialBroker { + // Request an atunnel certificate for the given actor. + // + // TODO(identity): Rename to MintAtunnelCertificate, as distinct from + // MintActorCertificate (which would be used for certificates projected into + // the actor filesystem, when/if we support those). rpc MintActorCertificate(MintActorCertificateRequest) returns (MintActorCertificateResponse) {} } message MintActorCertificateRequest { + // The actor for which the certificate should be issued. + string actor_atespace = 3; + string actor_name = 4; + + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + string actor_uid = 5; + // DER-encoded PKCS #10 certificate signing request. Atunnel retains the // corresponding private key. bytes certificate_signing_request = 1; - - // Actor incarnation this activation expects. Ateapi resolves the actor from - // the authenticated worker and rejects the request if its UID differs. - string expected_actor_uid = 2; } message MintActorCertificateResponse { diff --git a/internal/proto/ateletpb/atelet_grpc.pb.go b/internal/proto/ateletpb/atelet_grpc.pb.go index e82cc05b25..57954c019c 100644 --- a/internal/proto/ateletpb/atelet_grpc.pb.go +++ b/internal/proto/ateletpb/atelet_grpc.pb.go @@ -40,8 +40,15 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // -// CredentialBroker gives an authenticated worker its current actor credential. +// CredentialBroker provides on-demand services to ateom. +// +// TODO(identity): Rename to something more generic like AteomSupportService. type CredentialBrokerClient interface { + // Request an atunnel certificate for the given actor. + // + // TODO(identity): Rename to MintAtunnelCertificate, as distinct from + // MintActorCertificate (which would be used for certificates projected into + // the actor filesystem, when/if we support those). MintActorCertificate(ctx context.Context, in *MintActorCertificateRequest, opts ...grpc.CallOption) (*MintActorCertificateResponse, error) } @@ -67,8 +74,15 @@ func (c *credentialBrokerClient) MintActorCertificate(ctx context.Context, in *M // All implementations must embed UnimplementedCredentialBrokerServer // for forward compatibility. // -// CredentialBroker gives an authenticated worker its current actor credential. +// CredentialBroker provides on-demand services to ateom. +// +// TODO(identity): Rename to something more generic like AteomSupportService. type CredentialBrokerServer interface { + // Request an atunnel certificate for the given actor. + // + // TODO(identity): Rename to MintAtunnelCertificate, as distinct from + // MintActorCertificate (which would be used for certificates projected into + // the actor filesystem, when/if we support those). MintActorCertificate(context.Context, *MintActorCertificateRequest) (*MintActorCertificateResponse, error) mustEmbedUnimplementedCredentialBrokerServer() } diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index fd5cd3ba1b..e6f6409369 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -368,100 +368,100 @@ func (ActorMetadataField) EnumDescriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{5} } -type WorkerState int32 +type ActorCertificatePurpose int32 const ( - WorkerState_WORKER_STATE_UNSPECIFIED WorkerState = 0 - // Ready; schedulable. - WorkerState_WORKER_STATE_ACTIVE WorkerState = 1 - // Pod terminating. Not schedulable. - WorkerState_WORKER_STATE_DRAINING WorkerState = 2 // Keep this in sync with WorkerStatus.state's maximum. + ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED ActorCertificatePurpose = 0 + ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL ActorCertificatePurpose = 1 // Keep this in sync with MintCertRequest.purpose's maximum. ) -// Enum value maps for WorkerState. +// Enum value maps for ActorCertificatePurpose. var ( - WorkerState_name = map[int32]string{ - 0: "WORKER_STATE_UNSPECIFIED", - 1: "WORKER_STATE_ACTIVE", - 2: "WORKER_STATE_DRAINING", + ActorCertificatePurpose_name = map[int32]string{ + 0: "ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED", + 1: "ACTOR_CERTIFICATE_PURPOSE_ATUNNEL", } - WorkerState_value = map[string]int32{ - "WORKER_STATE_UNSPECIFIED": 0, - "WORKER_STATE_ACTIVE": 1, - "WORKER_STATE_DRAINING": 2, + ActorCertificatePurpose_value = map[string]int32{ + "ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED": 0, + "ACTOR_CERTIFICATE_PURPOSE_ATUNNEL": 1, } ) -func (x WorkerState) Enum() *WorkerState { - p := new(WorkerState) +func (x ActorCertificatePurpose) Enum() *ActorCertificatePurpose { + p := new(ActorCertificatePurpose) *p = x return p } -func (x WorkerState) String() string { +func (x ActorCertificatePurpose) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (WorkerState) Descriptor() protoreflect.EnumDescriptor { +func (ActorCertificatePurpose) Descriptor() protoreflect.EnumDescriptor { return file_ateapi_proto_enumTypes[6].Descriptor() } -func (WorkerState) Type() protoreflect.EnumType { +func (ActorCertificatePurpose) Type() protoreflect.EnumType { return &file_ateapi_proto_enumTypes[6] } -func (x WorkerState) Number() protoreflect.EnumNumber { +func (x ActorCertificatePurpose) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use WorkerState.Descriptor instead. -func (WorkerState) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use ActorCertificatePurpose.Descriptor instead. +func (ActorCertificatePurpose) EnumDescriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{6} } -type ActorCertificatePurpose int32 +type WorkerState int32 const ( - ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED ActorCertificatePurpose = 0 - ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_ATUNNEL ActorCertificatePurpose = 1 // Keep this in sync with MintCertRequest.purpose's maximum. + WorkerState_WORKER_STATE_UNSPECIFIED WorkerState = 0 + // Ready; schedulable. + WorkerState_WORKER_STATE_ACTIVE WorkerState = 1 + // Pod terminating. Not schedulable. + WorkerState_WORKER_STATE_DRAINING WorkerState = 2 // Keep this in sync with WorkerStatus.state's maximum. ) -// Enum value maps for ActorCertificatePurpose. +// Enum value maps for WorkerState. var ( - ActorCertificatePurpose_name = map[int32]string{ - 0: "ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED", - 1: "ACTOR_CERTIFICATE_PURPOSE_ATUNNEL", + WorkerState_name = map[int32]string{ + 0: "WORKER_STATE_UNSPECIFIED", + 1: "WORKER_STATE_ACTIVE", + 2: "WORKER_STATE_DRAINING", } - ActorCertificatePurpose_value = map[string]int32{ - "ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED": 0, - "ACTOR_CERTIFICATE_PURPOSE_ATUNNEL": 1, + WorkerState_value = map[string]int32{ + "WORKER_STATE_UNSPECIFIED": 0, + "WORKER_STATE_ACTIVE": 1, + "WORKER_STATE_DRAINING": 2, } ) -func (x ActorCertificatePurpose) Enum() *ActorCertificatePurpose { - p := new(ActorCertificatePurpose) +func (x WorkerState) Enum() *WorkerState { + p := new(WorkerState) *p = x return p } -func (x ActorCertificatePurpose) String() string { +func (x WorkerState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (ActorCertificatePurpose) Descriptor() protoreflect.EnumDescriptor { +func (WorkerState) Descriptor() protoreflect.EnumDescriptor { return file_ateapi_proto_enumTypes[7].Descriptor() } -func (ActorCertificatePurpose) Type() protoreflect.EnumType { +func (WorkerState) Type() protoreflect.EnumType { return &file_ateapi_proto_enumTypes[7] } -func (x ActorCertificatePurpose) Number() protoreflect.EnumNumber { +func (x WorkerState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use ActorCertificatePurpose.Descriptor instead. -func (ActorCertificatePurpose) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use WorkerState.Descriptor instead. +func (WorkerState) EnumDescriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{7} } @@ -4960,28 +4960,46 @@ func (x *DeleteActorEgressPolicyRequest) GetActor() *ObjectRef { return nil } -type GetActorSnapshotRequest struct { +type MintActorJWTRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // +k8s:opaqueType - ActorSnapshot *ObjectRef `protobuf:"bytes,1,opt,name=actor_snapshot,json=actorSnapshot,proto3" json:"actor_snapshot,omitempty"` + // The actor for which the JWT should be issued. + // + // Must be a valid actor that currently exists according to the actor store. + // + // +k8s:required + Actor *ObjectRef `protobuf:"bytes,5,opt,name=actor,proto3" json:"actor,omitempty"` + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + // + // +k8s:required + // +k8s:format=k8s-uuid + ActorUid string `protobuf:"bytes,7,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + // The audiences the minted JWT is bound to. Tokens are only issued with + // audience bindings, so at least one is required. + // + // +k8s:required + // +k8s:maxItems=16 # guardrail; tokens realistically bind a handful of audiences + // +k8s:listType=set + // +k8s:eachVal=+k8s:maxLength=512 # audiences are caller-defined URIs; bound only + Audience []string `protobuf:"bytes,1,rep,name=audience,proto3" json:"audience,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *GetActorSnapshotRequest) Reset() { - *x = GetActorSnapshotRequest{} +func (x *MintActorJWTRequest) Reset() { + *x = MintActorJWTRequest{} mi := &file_ateapi_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetActorSnapshotRequest) String() string { +func (x *MintActorJWTRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetActorSnapshotRequest) ProtoMessage() {} +func (*MintActorJWTRequest) ProtoMessage() {} -func (x *GetActorSnapshotRequest) ProtoReflect() protoreflect.Message { +func (x *MintActorJWTRequest) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4993,40 +5011,72 @@ func (x *GetActorSnapshotRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetActorSnapshotRequest.ProtoReflect.Descriptor instead. -func (*GetActorSnapshotRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use MintActorJWTRequest.ProtoReflect.Descriptor instead. +func (*MintActorJWTRequest) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{67} } -func (x *GetActorSnapshotRequest) GetActorSnapshot() *ObjectRef { +func (x *MintActorJWTRequest) GetActor() *ObjectRef { if x != nil { - return x.ActorSnapshot + return x.Actor } return nil } -type GetActorSnapshotTagRequest struct { +func (x *MintActorJWTRequest) GetActorUid() string { + if x != nil { + return x.ActorUid + } + return "" +} + +func (x *MintActorJWTRequest) GetAudience() []string { + if x != nil { + return x.Audience + } + return nil +} + +// TODO: check why k8s do ":" and not "/" as a seprator for the Subject format +// TODO: whats the right format for the subject? kubernetes follow "system:serviceaccount::". +type MintActorJWTResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - // +k8s:opaqueType - ActorSnapshotTag *ObjectRef `protobuf:"bytes,1,opt,name=actor_snapshot_tag,json=actorSnapshotTag,proto3" json:"actor_snapshot_tag,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Actor JWT. An OIDC Discovery-compatible JWT + // + // Claims: + // + // * iss: Issuer - a valid URL where a relying party can fetch the OIDC + // discovery documents. + // * sub: Subject - a string expressing the identity carried in the + // credential. Format + // `atespaces:${atespace}:actors:${actorname}`. + // * aud: Audience - a string identifying the service this token will be used + // to authenticate to. + // * nbf: Not Before - a numeric unix timestamp + // * exp: Expiration - a numeric unix timestamp + // * iat: Issued At - a numeric unix timestamp + // * `ate.dev`: Ate/Substrate Extension - JSON object + // * atespace: (string) The atespace the actor belongs to + // * actorName: (string) The actor's name, unique within its atespace + ActorJwt string `protobuf:"bytes,1,opt,name=actor_jwt,json=actorJwt,proto3" json:"actor_jwt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *GetActorSnapshotTagRequest) Reset() { - *x = GetActorSnapshotTagRequest{} +func (x *MintActorJWTResponse) Reset() { + *x = MintActorJWTResponse{} mi := &file_ateapi_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetActorSnapshotTagRequest) String() string { +func (x *MintActorJWTResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetActorSnapshotTagRequest) ProtoMessage() {} +func (*MintActorJWTResponse) ProtoMessage() {} -func (x *GetActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { +func (x *MintActorJWTResponse) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5038,41 +5088,61 @@ func (x *GetActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetActorSnapshotTagRequest.ProtoReflect.Descriptor instead. -func (*GetActorSnapshotTagRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use MintActorJWTResponse.ProtoReflect.Descriptor instead. +func (*MintActorJWTResponse) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{68} } -func (x *GetActorSnapshotTagRequest) GetActorSnapshotTag() *ObjectRef { +func (x *MintActorJWTResponse) GetActorJwt() string { if x != nil { - return x.ActorSnapshotTag + return x.ActorJwt } - return nil + return "" } -type ListActorSnapshotsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` +type MintActorCertificateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The actor for which the certificate should be issued. + // + // Must be a valid actor that currently exists according to the actor store. + // + // +k8s:required + Actor *ObjectRef `protobuf:"bytes,6,opt,name=actor,proto3" json:"actor,omitempty"` + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + // + // +k8s:required + // +k8s:format=k8s-uuid + ActorUid string `protobuf:"bytes,7,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + // Request contains DER encoded bytes of a x509 certificate signing request. + // The signer will ignore the contents of the CSR except to extract the + // subject public key. + // + // +k8s:required + // +k8s:customValidation # size bound; maxLength is string-only + CertificateSigningRequest []byte `protobuf:"bytes,2,opt,name=certificate_signing_request,json=certificateSigningRequest,proto3" json:"certificate_signing_request,omitempty"` + // +k8s:required + // +k8s:minimum=1 + // +k8s:maximum=1 # keep this in sync with the ActorCertificatePurpose enum + Purpose ActorCertificatePurpose `protobuf:"varint,4,opt,name=purpose,proto3,enum=ateapi.ActorCertificatePurpose" json:"purpose,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListActorSnapshotsRequest) Reset() { - *x = ListActorSnapshotsRequest{} +func (x *MintActorCertificateRequest) Reset() { + *x = MintActorCertificateRequest{} mi := &file_ateapi_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListActorSnapshotsRequest) String() string { +func (x *MintActorCertificateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListActorSnapshotsRequest) ProtoMessage() {} +func (*MintActorCertificateRequest) ProtoMessage() {} -func (x *ListActorSnapshotsRequest) ProtoReflect() protoreflect.Message { +func (x *MintActorCertificateRequest) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5084,54 +5154,63 @@ func (x *ListActorSnapshotsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListActorSnapshotsRequest.ProtoReflect.Descriptor instead. -func (*ListActorSnapshotsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use MintActorCertificateRequest.ProtoReflect.Descriptor instead. +func (*MintActorCertificateRequest) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{69} } -func (x *ListActorSnapshotsRequest) GetAtespace() string { +func (x *MintActorCertificateRequest) GetActor() *ObjectRef { if x != nil { - return x.Atespace + return x.Actor + } + return nil +} + +func (x *MintActorCertificateRequest) GetActorUid() string { + if x != nil { + return x.ActorUid } return "" } -func (x *ListActorSnapshotsRequest) GetPageSize() int32 { +func (x *MintActorCertificateRequest) GetCertificateSigningRequest() []byte { if x != nil { - return x.PageSize + return x.CertificateSigningRequest } - return 0 + return nil } -func (x *ListActorSnapshotsRequest) GetPageToken() string { +func (x *MintActorCertificateRequest) GetPurpose() ActorCertificatePurpose { if x != nil { - return x.PageToken + return x.Purpose } - return "" + return ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED } -type ListActorSnapshotsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - ActorSnapshots []*ActorSnapshot `protobuf:"bytes,1,rep,name=actor_snapshots,json=actorSnapshots,proto3" json:"actor_snapshots,omitempty"` - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +type MintActorCertificateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Response contains a list of DER encoded certificates. The first entry is the + // leaf certificate, and any remaining entries are intermediates in + // leaf-to-root order. + ActorCertificates [][]byte `protobuf:"bytes,1,rep,name=actor_certificates,json=actorCertificates,proto3" json:"actor_certificates,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *ListActorSnapshotsResponse) Reset() { - *x = ListActorSnapshotsResponse{} +func (x *MintActorCertificateResponse) Reset() { + *x = MintActorCertificateResponse{} mi := &file_ateapi_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListActorSnapshotsResponse) String() string { +func (x *MintActorCertificateResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListActorSnapshotsResponse) ProtoMessage() {} +func (*MintActorCertificateResponse) ProtoMessage() {} -func (x *ListActorSnapshotsResponse) ProtoReflect() protoreflect.Message { +func (x *MintActorCertificateResponse) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5143,47 +5222,40 @@ func (x *ListActorSnapshotsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListActorSnapshotsResponse.ProtoReflect.Descriptor instead. -func (*ListActorSnapshotsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use MintActorCertificateResponse.ProtoReflect.Descriptor instead. +func (*MintActorCertificateResponse) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{70} } -func (x *ListActorSnapshotsResponse) GetActorSnapshots() []*ActorSnapshot { +func (x *MintActorCertificateResponse) GetActorCertificates() [][]byte { if x != nil { - return x.ActorSnapshots + return x.ActorCertificates } return nil } -func (x *ListActorSnapshotsResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -type CreateActorSnapshotTagRequest struct { +type GetActorSnapshotRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // The tag to create. - ActorSnapshotTag *ActorSnapshotTag `protobuf:"bytes,1,opt,name=actor_snapshot_tag,json=actorSnapshotTag,proto3" json:"actor_snapshot_tag,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // +k8s:opaqueType + ActorSnapshot *ObjectRef `protobuf:"bytes,1,opt,name=actor_snapshot,json=actorSnapshot,proto3" json:"actor_snapshot,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *CreateActorSnapshotTagRequest) Reset() { - *x = CreateActorSnapshotTagRequest{} +func (x *GetActorSnapshotRequest) Reset() { + *x = GetActorSnapshotRequest{} mi := &file_ateapi_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateActorSnapshotTagRequest) String() string { +func (x *GetActorSnapshotRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateActorSnapshotTagRequest) ProtoMessage() {} +func (*GetActorSnapshotRequest) ProtoMessage() {} -func (x *CreateActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { +func (x *GetActorSnapshotRequest) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5195,9 +5267,211 @@ func (x *CreateActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } +// Deprecated: Use GetActorSnapshotRequest.ProtoReflect.Descriptor instead. +func (*GetActorSnapshotRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{71} +} + +func (x *GetActorSnapshotRequest) GetActorSnapshot() *ObjectRef { + if x != nil { + return x.ActorSnapshot + } + return nil +} + +type GetActorSnapshotTagRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // +k8s:opaqueType + ActorSnapshotTag *ObjectRef `protobuf:"bytes,1,opt,name=actor_snapshot_tag,json=actorSnapshotTag,proto3" json:"actor_snapshot_tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetActorSnapshotTagRequest) Reset() { + *x = GetActorSnapshotTagRequest{} + mi := &file_ateapi_proto_msgTypes[72] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetActorSnapshotTagRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetActorSnapshotTagRequest) ProtoMessage() {} + +func (x *GetActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[72] + 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 GetActorSnapshotTagRequest.ProtoReflect.Descriptor instead. +func (*GetActorSnapshotTagRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{72} +} + +func (x *GetActorSnapshotTagRequest) GetActorSnapshotTag() *ObjectRef { + if x != nil { + return x.ActorSnapshotTag + } + return nil +} + +type ListActorSnapshotsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListActorSnapshotsRequest) Reset() { + *x = ListActorSnapshotsRequest{} + mi := &file_ateapi_proto_msgTypes[73] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListActorSnapshotsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListActorSnapshotsRequest) ProtoMessage() {} + +func (x *ListActorSnapshotsRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[73] + 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 ListActorSnapshotsRequest.ProtoReflect.Descriptor instead. +func (*ListActorSnapshotsRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{73} +} + +func (x *ListActorSnapshotsRequest) GetAtespace() string { + if x != nil { + return x.Atespace + } + return "" +} + +func (x *ListActorSnapshotsRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListActorSnapshotsRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +type ListActorSnapshotsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + ActorSnapshots []*ActorSnapshot `protobuf:"bytes,1,rep,name=actor_snapshots,json=actorSnapshots,proto3" json:"actor_snapshots,omitempty"` + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListActorSnapshotsResponse) Reset() { + *x = ListActorSnapshotsResponse{} + mi := &file_ateapi_proto_msgTypes[74] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListActorSnapshotsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListActorSnapshotsResponse) ProtoMessage() {} + +func (x *ListActorSnapshotsResponse) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[74] + 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 ListActorSnapshotsResponse.ProtoReflect.Descriptor instead. +func (*ListActorSnapshotsResponse) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{74} +} + +func (x *ListActorSnapshotsResponse) GetActorSnapshots() []*ActorSnapshot { + if x != nil { + return x.ActorSnapshots + } + return nil +} + +func (x *ListActorSnapshotsResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +type CreateActorSnapshotTagRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The tag to create. + ActorSnapshotTag *ActorSnapshotTag `protobuf:"bytes,1,opt,name=actor_snapshot_tag,json=actorSnapshotTag,proto3" json:"actor_snapshot_tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateActorSnapshotTagRequest) Reset() { + *x = CreateActorSnapshotTagRequest{} + mi := &file_ateapi_proto_msgTypes[75] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateActorSnapshotTagRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateActorSnapshotTagRequest) ProtoMessage() {} + +func (x *CreateActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[75] + 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 CreateActorSnapshotTagRequest.ProtoReflect.Descriptor instead. func (*CreateActorSnapshotTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{71} + return file_ateapi_proto_rawDescGZIP(), []int{75} } func (x *CreateActorSnapshotTagRequest) GetActorSnapshotTag() *ActorSnapshotTag { @@ -5223,7 +5497,7 @@ type UpdateActorSnapshotTagRequest struct { func (x *UpdateActorSnapshotTagRequest) Reset() { *x = UpdateActorSnapshotTagRequest{} - mi := &file_ateapi_proto_msgTypes[72] + mi := &file_ateapi_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5235,7 +5509,7 @@ func (x *UpdateActorSnapshotTagRequest) String() string { func (*UpdateActorSnapshotTagRequest) ProtoMessage() {} func (x *UpdateActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[72] + mi := &file_ateapi_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5248,7 +5522,7 @@ func (x *UpdateActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActorSnapshotTagRequest.ProtoReflect.Descriptor instead. func (*UpdateActorSnapshotTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{72} + return file_ateapi_proto_rawDescGZIP(), []int{76} } func (x *UpdateActorSnapshotTagRequest) GetActorSnapshotTag() *ActorSnapshotTag { @@ -5268,7 +5542,7 @@ type DeleteActorSnapshotTagRequest struct { func (x *DeleteActorSnapshotTagRequest) Reset() { *x = DeleteActorSnapshotTagRequest{} - mi := &file_ateapi_proto_msgTypes[73] + mi := &file_ateapi_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5280,7 +5554,7 @@ func (x *DeleteActorSnapshotTagRequest) String() string { func (*DeleteActorSnapshotTagRequest) ProtoMessage() {} func (x *DeleteActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[73] + mi := &file_ateapi_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5293,7 +5567,7 @@ func (x *DeleteActorSnapshotTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorSnapshotTagRequest.ProtoReflect.Descriptor instead. func (*DeleteActorSnapshotTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{73} + return file_ateapi_proto_rawDescGZIP(), []int{77} } func (x *DeleteActorSnapshotTagRequest) GetActorSnapshotTag() *ObjectRef { @@ -5329,7 +5603,7 @@ type DeleteOptions struct { func (x *DeleteOptions) Reset() { *x = DeleteOptions{} - mi := &file_ateapi_proto_msgTypes[74] + mi := &file_ateapi_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5341,7 +5615,7 @@ func (x *DeleteOptions) String() string { func (*DeleteOptions) ProtoMessage() {} func (x *DeleteOptions) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[74] + mi := &file_ateapi_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5354,7 +5628,7 @@ func (x *DeleteOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteOptions.ProtoReflect.Descriptor instead. func (*DeleteOptions) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{74} + return file_ateapi_proto_rawDescGZIP(), []int{78} } func (x *DeleteOptions) GetVersion() int64 { @@ -5392,7 +5666,7 @@ type ListWorkersRequest struct { func (x *ListWorkersRequest) Reset() { *x = ListWorkersRequest{} - mi := &file_ateapi_proto_msgTypes[75] + mi := &file_ateapi_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5404,7 +5678,7 @@ func (x *ListWorkersRequest) String() string { func (*ListWorkersRequest) ProtoMessage() {} func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[75] + mi := &file_ateapi_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5417,7 +5691,7 @@ func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. func (*ListWorkersRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{75} + return file_ateapi_proto_rawDescGZIP(), []int{79} } func (x *ListWorkersRequest) GetPageSize() int32 { @@ -5446,7 +5720,7 @@ type ListWorkersResponse struct { func (x *ListWorkersResponse) Reset() { *x = ListWorkersResponse{} - mi := &file_ateapi_proto_msgTypes[76] + mi := &file_ateapi_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5458,7 +5732,7 @@ func (x *ListWorkersResponse) String() string { func (*ListWorkersResponse) ProtoMessage() {} func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[76] + mi := &file_ateapi_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5471,7 +5745,7 @@ func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. func (*ListWorkersResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{76} + return file_ateapi_proto_rawDescGZIP(), []int{80} } func (x *ListWorkersResponse) GetWorkers() []*Worker { @@ -5501,7 +5775,7 @@ type GetWorkerRequest struct { func (x *GetWorkerRequest) Reset() { *x = GetWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[77] + mi := &file_ateapi_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5513,7 +5787,7 @@ func (x *GetWorkerRequest) String() string { func (*GetWorkerRequest) ProtoMessage() {} func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[77] + mi := &file_ateapi_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5526,7 +5800,7 @@ func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkerRequest.ProtoReflect.Descriptor instead. func (*GetWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{77} + return file_ateapi_proto_rawDescGZIP(), []int{81} } func (x *GetWorkerRequest) GetWorker() *ObjectRef { @@ -5548,7 +5822,7 @@ type CreateWorkerRequest struct { func (x *CreateWorkerRequest) Reset() { *x = CreateWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[78] + mi := &file_ateapi_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5560,7 +5834,7 @@ func (x *CreateWorkerRequest) String() string { func (*CreateWorkerRequest) ProtoMessage() {} func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[78] + mi := &file_ateapi_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5573,7 +5847,7 @@ func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{78} + return file_ateapi_proto_rawDescGZIP(), []int{82} } func (x *CreateWorkerRequest) GetWorker() *Worker { @@ -5608,7 +5882,7 @@ type UpdateWorkerRequest struct { func (x *UpdateWorkerRequest) Reset() { *x = UpdateWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[79] + mi := &file_ateapi_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5620,7 +5894,7 @@ func (x *UpdateWorkerRequest) String() string { func (*UpdateWorkerRequest) ProtoMessage() {} func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[79] + mi := &file_ateapi_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5633,7 +5907,7 @@ func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkerRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{79} + return file_ateapi_proto_rawDescGZIP(), []int{83} } func (x *UpdateWorkerRequest) GetWorker() *Worker { @@ -5660,7 +5934,7 @@ type DeleteWorkerRequest struct { func (x *DeleteWorkerRequest) Reset() { *x = DeleteWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[80] + mi := &file_ateapi_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5672,7 +5946,7 @@ func (x *DeleteWorkerRequest) String() string { func (*DeleteWorkerRequest) ProtoMessage() {} func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[80] + mi := &file_ateapi_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5685,7 +5959,7 @@ func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkerRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{80} + return file_ateapi_proto_rawDescGZIP(), []int{84} } func (x *DeleteWorkerRequest) GetWorker() *ObjectRef { @@ -5715,7 +5989,7 @@ type DrainWorkerRequest struct { func (x *DrainWorkerRequest) Reset() { *x = DrainWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[81] + mi := &file_ateapi_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5727,7 +6001,7 @@ func (x *DrainWorkerRequest) String() string { func (*DrainWorkerRequest) ProtoMessage() {} func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[81] + mi := &file_ateapi_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5740,7 +6014,7 @@ func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerRequest.ProtoReflect.Descriptor instead. func (*DrainWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{81} + return file_ateapi_proto_rawDescGZIP(), []int{85} } func (x *DrainWorkerRequest) GetWorker() *ObjectRef { @@ -5778,7 +6052,7 @@ type ListActorsRequest struct { func (x *ListActorsRequest) Reset() { *x = ListActorsRequest{} - mi := &file_ateapi_proto_msgTypes[82] + mi := &file_ateapi_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5790,7 +6064,7 @@ func (x *ListActorsRequest) String() string { func (*ListActorsRequest) ProtoMessage() {} func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[82] + mi := &file_ateapi_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5803,7 +6077,7 @@ func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsRequest.ProtoReflect.Descriptor instead. func (*ListActorsRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{82} + return file_ateapi_proto_rawDescGZIP(), []int{86} } func (x *ListActorsRequest) GetAtespace() string { @@ -5839,7 +6113,7 @@ type ListActorsResponse struct { func (x *ListActorsResponse) Reset() { *x = ListActorsResponse{} - mi := &file_ateapi_proto_msgTypes[83] + mi := &file_ateapi_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5851,7 +6125,7 @@ func (x *ListActorsResponse) String() string { func (*ListActorsResponse) ProtoMessage() {} func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[83] + mi := &file_ateapi_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5864,7 +6138,7 @@ func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsResponse.ProtoReflect.Descriptor instead. func (*ListActorsResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{83} + return file_ateapi_proto_rawDescGZIP(), []int{87} } func (x *ListActorsResponse) GetActors() []*Actor { @@ -5961,7 +6235,7 @@ type Worker struct { func (x *Worker) Reset() { *x = Worker{} - mi := &file_ateapi_proto_msgTypes[84] + mi := &file_ateapi_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5973,7 +6247,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[84] + mi := &file_ateapi_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5986,7 +6260,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{84} + return file_ateapi_proto_rawDescGZIP(), []int{88} } func (x *Worker) GetMetadata() *ResourceMetadata { @@ -6082,7 +6356,7 @@ type WorkerStatus struct { func (x *WorkerStatus) Reset() { *x = WorkerStatus{} - mi := &file_ateapi_proto_msgTypes[85] + mi := &file_ateapi_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6094,7 +6368,7 @@ func (x *WorkerStatus) String() string { func (*WorkerStatus) ProtoMessage() {} func (x *WorkerStatus) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[85] + mi := &file_ateapi_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6107,7 +6381,7 @@ func (x *WorkerStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. func (*WorkerStatus) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{85} + return file_ateapi_proto_rawDescGZIP(), []int{89} } func (x *WorkerStatus) GetState() WorkerState { @@ -6148,7 +6422,7 @@ type WorkerCapacity struct { func (x *WorkerCapacity) Reset() { *x = WorkerCapacity{} - mi := &file_ateapi_proto_msgTypes[86] + mi := &file_ateapi_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6160,7 +6434,7 @@ func (x *WorkerCapacity) String() string { func (*WorkerCapacity) ProtoMessage() {} func (x *WorkerCapacity) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[86] + mi := &file_ateapi_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6173,7 +6447,7 @@ func (x *WorkerCapacity) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerCapacity.ProtoReflect.Descriptor instead. func (*WorkerCapacity) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{86} + return file_ateapi_proto_rawDescGZIP(), []int{90} } func (x *WorkerCapacity) GetCpuMilli() int64 { @@ -6212,7 +6486,7 @@ type ActorAssignment struct { func (x *ActorAssignment) Reset() { *x = ActorAssignment{} - mi := &file_ateapi_proto_msgTypes[87] + mi := &file_ateapi_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6224,7 +6498,7 @@ func (x *ActorAssignment) String() string { func (*ActorAssignment) ProtoMessage() {} func (x *ActorAssignment) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[87] + mi := &file_ateapi_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6237,7 +6511,7 @@ func (x *ActorAssignment) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorAssignment.ProtoReflect.Descriptor instead. func (*ActorAssignment) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{87} + return file_ateapi_proto_rawDescGZIP(), []int{91} } func (x *ActorAssignment) GetActor() *ObjectRef { @@ -6261,290 +6535,6 @@ func (x *ActorAssignment) GetActorTemplateRef() *ObjectRef { return nil } -type MintJWTRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The audiences the minted JWT is bound to. Tokens are only issued with - // audience bindings, so at least one is required. - // - // +k8s:required - // +k8s:maxItems=16 # guardrail; tokens realistically bind a handful of audiences - // +k8s:listType=set - // +k8s:eachVal=+k8s:maxLength=512 # audiences are caller-defined URIs; bound only - Audience []string `protobuf:"bytes,1,rep,name=audience,proto3" json:"audience,omitempty"` - // +k8s:required - // +k8s:format=k8s-short-name - Atespace string `protobuf:"bytes,2,opt,name=atespace,proto3" json:"atespace,omitempty"` - // +k8s:required - // +k8s:format=k8s-short-name - ActorName string `protobuf:"bytes,3,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` - // +k8s:optional - // +k8s:format=k8s-uuid - ActorUid string `protobuf:"bytes,4,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintJWTRequest) Reset() { - *x = MintJWTRequest{} - mi := &file_ateapi_proto_msgTypes[88] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintJWTRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintJWTRequest) ProtoMessage() {} - -func (x *MintJWTRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[88] - 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 MintJWTRequest.ProtoReflect.Descriptor instead. -func (*MintJWTRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{88} -} - -func (x *MintJWTRequest) GetAudience() []string { - if x != nil { - return x.Audience - } - return nil -} - -func (x *MintJWTRequest) GetAtespace() string { - if x != nil { - return x.Atespace - } - return "" -} - -func (x *MintJWTRequest) GetActorName() string { - if x != nil { - return x.ActorName - } - return "" -} - -func (x *MintJWTRequest) GetActorUid() string { - if x != nil { - return x.ActorUid - } - return "" -} - -// TODO: check why k8s do ":" and not "/" as a seprator for the Subject format -// TODO: whats the right format for the subject? kubernetes follow "system:serviceaccount::". -type MintJWTResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Actor JWT. An OIDC Discovery-compatible JWT - // - // Claims: - // - // * iss: Issuer - a valid URL where a relying party can fetch the OIDC - // discovery documents. - // * sub: Subject - a string expressing the identity carried in the - // credential. Format - // `atespaces:${atespace}:actors:${actorname}`. - // * aud: Audience - a string identifying the service this token will be used - // to authenticate to. - // * nbf: Not Before - a numeric unix timestamp - // * exp: Expiration - a numeric unix timestamp - // * iat: Issued At - a numeric unix timestamp - // * `ate.dev`: Ate/Substrate Extension - JSON object - // * atespace: (string) The atespace the actor belongs to - // * actorName: (string) The actor's name, unique within its atespace - ActorJwt string `protobuf:"bytes,1,opt,name=actor_jwt,json=actorJwt,proto3" json:"actor_jwt,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintJWTResponse) Reset() { - *x = MintJWTResponse{} - mi := &file_ateapi_proto_msgTypes[89] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintJWTResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintJWTResponse) ProtoMessage() {} - -func (x *MintJWTResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[89] - 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 MintJWTResponse.ProtoReflect.Descriptor instead. -func (*MintJWTResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{89} -} - -func (x *MintJWTResponse) GetActorJwt() string { - if x != nil { - return x.ActorJwt - } - return "" -} - -type MintCertRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The Worker the certificate is minted for, as authenticated by the - // node-local atelet. Workers are global-scoped, so this carries no atespace. - // Ateapi resolves the worker's current actor assignment rather than trusting - // actor metadata from the caller. - // - // This is the one caller that recovers a Worker name from a pod certificate: - // the atelet has only the worker Pod's identity to go on. Everywhere else the - // name is opaque and must be carried, not reconstructed. - // - // +k8s:beta(since: "0.0")=+k8s:subfield(atespace)=+k8s:forbidden # TODO: get rid of beta prefix - // +k8s:required - Worker *ObjectRef `protobuf:"bytes,1,opt,name=worker,proto3" json:"worker,omitempty"` - // Request contains DER encoded bytes of a x509 certificate signing request. - // The signer will ignore the contents of the CSR except to extract the - // subject public key. - // - // +k8s:required - // +k8s:customValidation # size bound; maxLength is string-only - CertificateSigningRequest []byte `protobuf:"bytes,2,opt,name=certificate_signing_request,json=certificateSigningRequest,proto3" json:"certificate_signing_request,omitempty"` - // Actor incarnation expected by the activation. This is only a stale-request - // guard: ateapi derives the actor and its identity from the worker assignment. - // - // +k8s:required - // +k8s:format=k8s-uuid - ExpectedActorUid string `protobuf:"bytes,3,opt,name=expected_actor_uid,json=expectedActorUid,proto3" json:"expected_actor_uid,omitempty"` - // +k8s:required - // +k8s:minimum=1 - // +k8s:maximum=1 # keep this in sync with the ActorCertificatePurpose enum - Purpose ActorCertificatePurpose `protobuf:"varint,4,opt,name=purpose,proto3,enum=ateapi.ActorCertificatePurpose" json:"purpose,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintCertRequest) Reset() { - *x = MintCertRequest{} - mi := &file_ateapi_proto_msgTypes[90] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintCertRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintCertRequest) ProtoMessage() {} - -func (x *MintCertRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[90] - 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 MintCertRequest.ProtoReflect.Descriptor instead. -func (*MintCertRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{90} -} - -func (x *MintCertRequest) GetWorker() *ObjectRef { - if x != nil { - return x.Worker - } - return nil -} - -func (x *MintCertRequest) GetCertificateSigningRequest() []byte { - if x != nil { - return x.CertificateSigningRequest - } - return nil -} - -func (x *MintCertRequest) GetExpectedActorUid() string { - if x != nil { - return x.ExpectedActorUid - } - return "" -} - -func (x *MintCertRequest) GetPurpose() ActorCertificatePurpose { - if x != nil { - return x.Purpose - } - return ActorCertificatePurpose_ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED -} - -type MintCertResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Response contains a list of DER encoded certificates. The first entry is the - // leaf certificate, and any remaining entries are intermediates in - // leaf-to-root order. - ActorCertificates [][]byte `protobuf:"bytes,1,rep,name=actor_certificates,json=actorCertificates,proto3" json:"actor_certificates,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintCertResponse) Reset() { - *x = MintCertResponse{} - mi := &file_ateapi_proto_msgTypes[91] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintCertResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintCertResponse) ProtoMessage() {} - -func (x *MintCertResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[91] - 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 MintCertResponse.ProtoReflect.Descriptor instead. -func (*MintCertResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{91} -} - -func (x *MintCertResponse) GetActorCertificates() [][]byte { - if x != nil { - return x.ActorCertificates - } - return nil -} - var File_ateapi_proto protoreflect.FileDescriptor const file_ateapi_proto_rawDesc = "" + @@ -6799,7 +6789,20 @@ const file_ateapi_proto_rawDesc = "" + "\x05actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x129\n" + "\regress_policy\x18\x02 \x01(\v2\x14.ateapi.EgressPolicyR\fegressPolicy\"I\n" + "\x1eDeleteActorEgressPolicyRequest\x12'\n" + - "\x05actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\"S\n" + + "\x05actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\"w\n" + + "\x13MintActorJWTRequest\x12'\n" + + "\x05actor\x18\x05 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x12\x1b\n" + + "\tactor_uid\x18\a \x01(\tR\bactorUid\x12\x1a\n" + + "\baudience\x18\x01 \x03(\tR\baudience\"3\n" + + "\x14MintActorJWTResponse\x12\x1b\n" + + "\tactor_jwt\x18\x01 \x01(\tR\bactorJwt\"\xde\x01\n" + + "\x1bMintActorCertificateRequest\x12'\n" + + "\x05actor\x18\x06 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x12\x1b\n" + + "\tactor_uid\x18\a \x01(\tR\bactorUid\x12>\n" + + "\x1bcertificate_signing_request\x18\x02 \x01(\fR\x19certificateSigningRequest\x129\n" + + "\apurpose\x18\x04 \x01(\x0e2\x1f.ateapi.ActorCertificatePurposeR\apurpose\"M\n" + + "\x1cMintActorCertificateResponse\x12-\n" + + "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates\"S\n" + "\x17GetActorSnapshotRequest\x128\n" + "\x0eactor_snapshot\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\ractorSnapshot\"]\n" + "\x1aGetActorSnapshotTagRequest\x12?\n" + @@ -6876,22 +6879,7 @@ const file_ateapi_proto_rawDesc = "" + "\x0fActorAssignment\x12'\n" + "\x05actor\x18\x02 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x12\x1b\n" + "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x12?\n" + - "\x12actor_template_ref\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\x10actorTemplateRef\"\x84\x01\n" + - "\x0eMintJWTRequest\x12\x1a\n" + - "\baudience\x18\x01 \x03(\tR\baudience\x12\x1a\n" + - "\batespace\x18\x02 \x01(\tR\batespace\x12\x1d\n" + - "\n" + - "actor_name\x18\x03 \x01(\tR\tactorName\x12\x1b\n" + - "\tactor_uid\x18\x04 \x01(\tR\bactorUid\".\n" + - "\x0fMintJWTResponse\x12\x1b\n" + - "\tactor_jwt\x18\x01 \x01(\tR\bactorJwt\"\xe5\x01\n" + - "\x0fMintCertRequest\x12)\n" + - "\x06worker\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x06worker\x12>\n" + - "\x1bcertificate_signing_request\x18\x02 \x01(\fR\x19certificateSigningRequest\x12,\n" + - "\x12expected_actor_uid\x18\x03 \x01(\tR\x10expectedActorUid\x129\n" + - "\apurpose\x18\x04 \x01(\x0e2\x1f.ateapi.ActorCertificatePurposeR\apurpose\"A\n" + - "\x10MintCertResponse\x12-\n" + - "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates*\x80\x01\n" + + "\x12actor_template_ref\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\x10actorTemplateRef*\x80\x01\n" + "\x14SnapshotContentScope\x12&\n" + "\"SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED\x10\x00\x12\x1f\n" + "\x1bSNAPSHOT_CONTENT_SCOPE_FULL\x10\x01\x12\x1f\n" + @@ -6923,14 +6911,14 @@ const file_ateapi_proto_rawDesc = "" + " ACTOR_METADATA_FIELD_UNSPECIFIED\x10\x00\x12\x1d\n" + "\x19ACTOR_METADATA_FIELD_NAME\x10\x01\x12!\n" + "\x1dACTOR_METADATA_FIELD_ATESPACE\x10\x02\x12\x1c\n" + - "\x18ACTOR_METADATA_FIELD_UID\x10\x03*_\n" + + "\x18ACTOR_METADATA_FIELD_UID\x10\x03*k\n" + + "\x17ActorCertificatePurpose\x12)\n" + + "%ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED\x10\x00\x12%\n" + + "!ACTOR_CERTIFICATE_PURPOSE_ATUNNEL\x10\x01*_\n" + "\vWorkerState\x12\x1c\n" + "\x18WORKER_STATE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13WORKER_STATE_ACTIVE\x10\x01\x12\x19\n" + - "\x15WORKER_STATE_DRAINING\x10\x02*k\n" + - "\x17ActorCertificatePurpose\x12)\n" + - "%ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED\x10\x00\x12%\n" + - "!ACTOR_CERTIFICATE_PURPOSE_ATUNNEL\x10\x012\x84\x13\n" + + "\x15WORKER_STATE_DRAINING\x10\x022\xb6\x14\n" + "\aControl\x124\n" + "\bGetActor\x12\x17.ateapi.GetActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n" + "\vCreateActor\x12\x1a.ateapi.CreateActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n" + @@ -6943,7 +6931,9 @@ const file_ateapi_proto_rawDesc = "" + "\x14GetActorEgressPolicy\x12#.ateapi.GetActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n" + "\x17CreateActorEgressPolicy\x12&.ateapi.CreateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n" + "\x17UpdateActorEgressPolicy\x12&.ateapi.UpdateActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12Y\n" + - "\x17DeleteActorEgressPolicy\x12&.ateapi.DeleteActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12L\n" + + "\x17DeleteActorEgressPolicy\x12&.ateapi.DeleteActorEgressPolicyRequest\x1a\x14.ateapi.EgressPolicy\"\x00\x12K\n" + + "\fMintActorJWT\x12\x1b.ateapi.MintActorJWTRequest\x1a\x1c.ateapi.MintActorJWTResponse\"\x00\x12c\n" + + "\x14MintActorCertificate\x12#.ateapi.MintActorCertificateRequest\x1a$.ateapi.MintActorCertificateResponse\"\x00\x12L\n" + "\x10GetActorSnapshot\x12\x1f.ateapi.GetActorSnapshotRequest\x1a\x15.ateapi.ActorSnapshot\"\x00\x12U\n" + "\x13GetActorSnapshotTag\x12\".ateapi.GetActorSnapshotTagRequest\x1a\x18.ateapi.ActorSnapshotTag\"\x00\x12]\n" + "\x12ListActorSnapshots\x12!.ateapi.ListActorSnapshotsRequest\x1a\".ateapi.ListActorSnapshotsResponse\"\x00\x12[\n" + @@ -6965,10 +6955,7 @@ const file_ateapi_proto_rawDesc = "" + "\x13CreateActorTemplate\x12\".ateapi.CreateActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12L\n" + "\x10GetActorTemplate\x12\x1f.ateapi.GetActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00\x12]\n" + "\x12ListActorTemplates\x12!.ateapi.ListActorTemplatesRequest\x1a\".ateapi.ListActorTemplatesResponse\"\x00\x12R\n" + - "\x13DeleteActorTemplate\x12\".ateapi.DeleteActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x002\x8a\x01\n" + - "\rActorIdentity\x12:\n" + - "\aMintJWT\x12\x16.ateapi.MintJWTRequest\x1a\x17.ateapi.MintJWTResponse\x12=\n" + - "\bMintCert\x12\x17.ateapi.MintCertRequest\x1a\x18.ateapi.MintCertResponseB9Z7github.com/agent-substrate/substrate/pkg/proto/ateapipbb\x06proto3" + "\x13DeleteActorTemplate\x12\".ateapi.DeleteActorTemplateRequest\x1a\x15.ateapi.ActorTemplate\"\x00B9Z7github.com/agent-substrate/substrate/pkg/proto/ateapipbb\x06proto3" var ( file_ateapi_proto_rawDescOnce sync.Once @@ -6991,8 +6978,8 @@ var file_ateapi_proto_goTypes = []any{ (SandboxClass)(0), // 3: ateapi.SandboxClass (ResumeSource)(0), // 4: ateapi.ResumeSource (ActorMetadataField)(0), // 5: ateapi.ActorMetadataField - (WorkerState)(0), // 6: ateapi.WorkerState - (ActorCertificatePurpose)(0), // 7: ateapi.ActorCertificatePurpose + (ActorCertificatePurpose)(0), // 6: ateapi.ActorCertificatePurpose + (WorkerState)(0), // 7: ateapi.WorkerState (ExternalVolume_Status)(0), // 8: ateapi.ExternalVolume.Status (*LocalSnapshotInfo)(nil), // 9: ateapi.LocalSnapshotInfo (*Selector)(nil), // 10: ateapi.Selector @@ -7061,31 +7048,31 @@ var file_ateapi_proto_goTypes = []any{ (*CreateActorEgressPolicyRequest)(nil), // 73: ateapi.CreateActorEgressPolicyRequest (*UpdateActorEgressPolicyRequest)(nil), // 74: ateapi.UpdateActorEgressPolicyRequest (*DeleteActorEgressPolicyRequest)(nil), // 75: ateapi.DeleteActorEgressPolicyRequest - (*GetActorSnapshotRequest)(nil), // 76: ateapi.GetActorSnapshotRequest - (*GetActorSnapshotTagRequest)(nil), // 77: ateapi.GetActorSnapshotTagRequest - (*ListActorSnapshotsRequest)(nil), // 78: ateapi.ListActorSnapshotsRequest - (*ListActorSnapshotsResponse)(nil), // 79: ateapi.ListActorSnapshotsResponse - (*CreateActorSnapshotTagRequest)(nil), // 80: ateapi.CreateActorSnapshotTagRequest - (*UpdateActorSnapshotTagRequest)(nil), // 81: ateapi.UpdateActorSnapshotTagRequest - (*DeleteActorSnapshotTagRequest)(nil), // 82: ateapi.DeleteActorSnapshotTagRequest - (*DeleteOptions)(nil), // 83: ateapi.DeleteOptions - (*ListWorkersRequest)(nil), // 84: ateapi.ListWorkersRequest - (*ListWorkersResponse)(nil), // 85: ateapi.ListWorkersResponse - (*GetWorkerRequest)(nil), // 86: ateapi.GetWorkerRequest - (*CreateWorkerRequest)(nil), // 87: ateapi.CreateWorkerRequest - (*UpdateWorkerRequest)(nil), // 88: ateapi.UpdateWorkerRequest - (*DeleteWorkerRequest)(nil), // 89: ateapi.DeleteWorkerRequest - (*DrainWorkerRequest)(nil), // 90: ateapi.DrainWorkerRequest - (*ListActorsRequest)(nil), // 91: ateapi.ListActorsRequest - (*ListActorsResponse)(nil), // 92: ateapi.ListActorsResponse - (*Worker)(nil), // 93: ateapi.Worker - (*WorkerStatus)(nil), // 94: ateapi.WorkerStatus - (*WorkerCapacity)(nil), // 95: ateapi.WorkerCapacity - (*ActorAssignment)(nil), // 96: ateapi.ActorAssignment - (*MintJWTRequest)(nil), // 97: ateapi.MintJWTRequest - (*MintJWTResponse)(nil), // 98: ateapi.MintJWTResponse - (*MintCertRequest)(nil), // 99: ateapi.MintCertRequest - (*MintCertResponse)(nil), // 100: ateapi.MintCertResponse + (*MintActorJWTRequest)(nil), // 76: ateapi.MintActorJWTRequest + (*MintActorJWTResponse)(nil), // 77: ateapi.MintActorJWTResponse + (*MintActorCertificateRequest)(nil), // 78: ateapi.MintActorCertificateRequest + (*MintActorCertificateResponse)(nil), // 79: ateapi.MintActorCertificateResponse + (*GetActorSnapshotRequest)(nil), // 80: ateapi.GetActorSnapshotRequest + (*GetActorSnapshotTagRequest)(nil), // 81: ateapi.GetActorSnapshotTagRequest + (*ListActorSnapshotsRequest)(nil), // 82: ateapi.ListActorSnapshotsRequest + (*ListActorSnapshotsResponse)(nil), // 83: ateapi.ListActorSnapshotsResponse + (*CreateActorSnapshotTagRequest)(nil), // 84: ateapi.CreateActorSnapshotTagRequest + (*UpdateActorSnapshotTagRequest)(nil), // 85: ateapi.UpdateActorSnapshotTagRequest + (*DeleteActorSnapshotTagRequest)(nil), // 86: ateapi.DeleteActorSnapshotTagRequest + (*DeleteOptions)(nil), // 87: ateapi.DeleteOptions + (*ListWorkersRequest)(nil), // 88: ateapi.ListWorkersRequest + (*ListWorkersResponse)(nil), // 89: ateapi.ListWorkersResponse + (*GetWorkerRequest)(nil), // 90: ateapi.GetWorkerRequest + (*CreateWorkerRequest)(nil), // 91: ateapi.CreateWorkerRequest + (*UpdateWorkerRequest)(nil), // 92: ateapi.UpdateWorkerRequest + (*DeleteWorkerRequest)(nil), // 93: ateapi.DeleteWorkerRequest + (*DrainWorkerRequest)(nil), // 94: ateapi.DrainWorkerRequest + (*ListActorsRequest)(nil), // 95: ateapi.ListActorsRequest + (*ListActorsResponse)(nil), // 96: ateapi.ListActorsResponse + (*Worker)(nil), // 97: ateapi.Worker + (*WorkerStatus)(nil), // 98: ateapi.WorkerStatus + (*WorkerCapacity)(nil), // 99: ateapi.WorkerCapacity + (*ActorAssignment)(nil), // 100: ateapi.ActorAssignment nil, // 101: ateapi.Selector.MatchLabelsEntry nil, // 102: ateapi.ExternalVolume.VolumeContextEntry nil, // 103: ateapi.Worker.LabelsEntry @@ -7185,103 +7172,104 @@ var file_ateapi_proto_depIdxs = []int32{ 27, // 89: ateapi.UpdateActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef 14, // 90: ateapi.UpdateActorEgressPolicyRequest.egress_policy:type_name -> ateapi.EgressPolicy 27, // 91: ateapi.DeleteActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef - 27, // 92: ateapi.GetActorSnapshotRequest.actor_snapshot:type_name -> ateapi.ObjectRef - 27, // 93: ateapi.GetActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ObjectRef - 23, // 94: ateapi.ListActorSnapshotsResponse.actor_snapshots:type_name -> ateapi.ActorSnapshot - 25, // 95: ateapi.CreateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag - 25, // 96: ateapi.UpdateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag - 27, // 97: ateapi.DeleteActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ObjectRef - 93, // 98: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker - 27, // 99: ateapi.GetWorkerRequest.worker:type_name -> ateapi.ObjectRef - 93, // 100: ateapi.CreateWorkerRequest.worker:type_name -> ateapi.Worker - 93, // 101: ateapi.UpdateWorkerRequest.worker:type_name -> ateapi.Worker - 27, // 102: ateapi.DeleteWorkerRequest.worker:type_name -> ateapi.ObjectRef - 83, // 103: ateapi.DeleteWorkerRequest.options:type_name -> ateapi.DeleteOptions - 27, // 104: ateapi.DrainWorkerRequest.worker:type_name -> ateapi.ObjectRef - 13, // 105: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor - 11, // 106: ateapi.Worker.metadata:type_name -> ateapi.ResourceMetadata - 103, // 107: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry - 95, // 108: ateapi.Worker.capacity:type_name -> ateapi.WorkerCapacity - 94, // 109: ateapi.Worker.status:type_name -> ateapi.WorkerStatus - 6, // 110: ateapi.WorkerStatus.state:type_name -> ateapi.WorkerState - 96, // 111: ateapi.WorkerStatus.assignment:type_name -> ateapi.ActorAssignment - 27, // 112: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef - 27, // 113: ateapi.ActorAssignment.actor_template_ref:type_name -> ateapi.ObjectRef - 27, // 114: ateapi.MintCertRequest.worker:type_name -> ateapi.ObjectRef - 7, // 115: ateapi.MintCertRequest.purpose:type_name -> ateapi.ActorCertificatePurpose - 62, // 116: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest - 63, // 117: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest - 64, // 118: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest - 65, // 119: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest - 67, // 120: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest - 69, // 121: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest - 71, // 122: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest - 72, // 123: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest - 73, // 124: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest - 74, // 125: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest - 75, // 126: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest - 76, // 127: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest - 77, // 128: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest - 78, // 129: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest - 80, // 130: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest - 81, // 131: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest - 82, // 132: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest - 84, // 133: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest - 86, // 134: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest - 87, // 135: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest - 88, // 136: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest - 89, // 137: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest - 90, // 138: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest - 91, // 139: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest - 52, // 140: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest - 53, // 141: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest - 54, // 142: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest - 56, // 143: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest - 57, // 144: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest - 58, // 145: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest - 59, // 146: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest - 61, // 147: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest - 97, // 148: ateapi.ActorIdentity.MintJWT:input_type -> ateapi.MintJWTRequest - 99, // 149: ateapi.ActorIdentity.MintCert:input_type -> ateapi.MintCertRequest - 13, // 150: ateapi.Control.GetActor:output_type -> ateapi.Actor - 13, // 151: ateapi.Control.CreateActor:output_type -> ateapi.Actor - 13, // 152: ateapi.Control.UpdateActor:output_type -> ateapi.Actor - 66, // 153: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse - 68, // 154: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse - 70, // 155: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse - 13, // 156: ateapi.Control.DeleteActor:output_type -> ateapi.Actor - 14, // 157: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy - 14, // 158: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy - 14, // 159: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy - 14, // 160: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy - 23, // 161: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot - 25, // 162: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 79, // 163: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse - 25, // 164: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 25, // 165: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 25, // 166: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 85, // 167: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse - 93, // 168: ateapi.Control.GetWorker:output_type -> ateapi.Worker - 93, // 169: ateapi.Control.CreateWorker:output_type -> ateapi.Worker - 93, // 170: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker - 93, // 171: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker - 93, // 172: ateapi.Control.DrainWorker:output_type -> ateapi.Worker - 92, // 173: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse - 26, // 174: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace - 26, // 175: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace - 55, // 176: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse - 26, // 177: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace - 28, // 178: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate - 28, // 179: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate - 60, // 180: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse - 28, // 181: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate - 98, // 182: ateapi.ActorIdentity.MintJWT:output_type -> ateapi.MintJWTResponse - 100, // 183: ateapi.ActorIdentity.MintCert:output_type -> ateapi.MintCertResponse - 150, // [150:184] is the sub-list for method output_type - 116, // [116:150] is the sub-list for method input_type - 116, // [116:116] is the sub-list for extension type_name - 116, // [116:116] is the sub-list for extension extendee - 0, // [0:116] is the sub-list for field type_name + 27, // 92: ateapi.MintActorJWTRequest.actor:type_name -> ateapi.ObjectRef + 27, // 93: ateapi.MintActorCertificateRequest.actor:type_name -> ateapi.ObjectRef + 6, // 94: ateapi.MintActorCertificateRequest.purpose:type_name -> ateapi.ActorCertificatePurpose + 27, // 95: ateapi.GetActorSnapshotRequest.actor_snapshot:type_name -> ateapi.ObjectRef + 27, // 96: ateapi.GetActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ObjectRef + 23, // 97: ateapi.ListActorSnapshotsResponse.actor_snapshots:type_name -> ateapi.ActorSnapshot + 25, // 98: ateapi.CreateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag + 25, // 99: ateapi.UpdateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag + 27, // 100: ateapi.DeleteActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ObjectRef + 97, // 101: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker + 27, // 102: ateapi.GetWorkerRequest.worker:type_name -> ateapi.ObjectRef + 97, // 103: ateapi.CreateWorkerRequest.worker:type_name -> ateapi.Worker + 97, // 104: ateapi.UpdateWorkerRequest.worker:type_name -> ateapi.Worker + 27, // 105: ateapi.DeleteWorkerRequest.worker:type_name -> ateapi.ObjectRef + 87, // 106: ateapi.DeleteWorkerRequest.options:type_name -> ateapi.DeleteOptions + 27, // 107: ateapi.DrainWorkerRequest.worker:type_name -> ateapi.ObjectRef + 13, // 108: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor + 11, // 109: ateapi.Worker.metadata:type_name -> ateapi.ResourceMetadata + 103, // 110: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry + 99, // 111: ateapi.Worker.capacity:type_name -> ateapi.WorkerCapacity + 98, // 112: ateapi.Worker.status:type_name -> ateapi.WorkerStatus + 7, // 113: ateapi.WorkerStatus.state:type_name -> ateapi.WorkerState + 100, // 114: ateapi.WorkerStatus.assignment:type_name -> ateapi.ActorAssignment + 27, // 115: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef + 27, // 116: ateapi.ActorAssignment.actor_template_ref:type_name -> ateapi.ObjectRef + 62, // 117: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest + 63, // 118: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest + 64, // 119: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest + 65, // 120: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest + 67, // 121: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest + 69, // 122: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest + 71, // 123: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest + 72, // 124: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest + 73, // 125: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest + 74, // 126: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest + 75, // 127: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest + 76, // 128: ateapi.Control.MintActorJWT:input_type -> ateapi.MintActorJWTRequest + 78, // 129: ateapi.Control.MintActorCertificate:input_type -> ateapi.MintActorCertificateRequest + 80, // 130: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest + 81, // 131: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest + 82, // 132: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest + 84, // 133: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest + 85, // 134: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest + 86, // 135: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest + 88, // 136: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest + 90, // 137: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest + 91, // 138: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest + 92, // 139: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest + 93, // 140: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest + 94, // 141: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest + 95, // 142: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest + 52, // 143: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest + 53, // 144: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest + 54, // 145: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest + 56, // 146: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest + 57, // 147: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest + 58, // 148: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest + 59, // 149: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest + 61, // 150: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest + 13, // 151: ateapi.Control.GetActor:output_type -> ateapi.Actor + 13, // 152: ateapi.Control.CreateActor:output_type -> ateapi.Actor + 13, // 153: ateapi.Control.UpdateActor:output_type -> ateapi.Actor + 66, // 154: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse + 68, // 155: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse + 70, // 156: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse + 13, // 157: ateapi.Control.DeleteActor:output_type -> ateapi.Actor + 14, // 158: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy + 14, // 159: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy + 14, // 160: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy + 14, // 161: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy + 77, // 162: ateapi.Control.MintActorJWT:output_type -> ateapi.MintActorJWTResponse + 79, // 163: ateapi.Control.MintActorCertificate:output_type -> ateapi.MintActorCertificateResponse + 23, // 164: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot + 25, // 165: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 83, // 166: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse + 25, // 167: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 25, // 168: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 25, // 169: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 89, // 170: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse + 97, // 171: ateapi.Control.GetWorker:output_type -> ateapi.Worker + 97, // 172: ateapi.Control.CreateWorker:output_type -> ateapi.Worker + 97, // 173: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker + 97, // 174: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker + 97, // 175: ateapi.Control.DrainWorker:output_type -> ateapi.Worker + 96, // 176: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse + 26, // 177: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace + 26, // 178: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace + 55, // 179: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse + 26, // 180: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace + 28, // 181: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate + 28, // 182: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate + 60, // 183: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse + 28, // 184: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate + 151, // [151:185] is the sub-list for method output_type + 117, // [117:151] is the sub-list for method input_type + 117, // [117:117] is the sub-list for extension type_name + 117, // [117:117] is the sub-list for extension extendee + 0, // [0:117] is the sub-list for field type_name } func init() { file_ateapi_proto_init() } @@ -7297,7 +7285,7 @@ func file_ateapi_proto_init() { NumEnums: 9, NumMessages: 95, NumExtensions: 0, - NumServices: 2, + NumServices: 1, }, GoTypes: file_ateapi_proto_goTypes, DependencyIndexes: file_ateapi_proto_depIdxs, diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index 038f76fe93..5ca72cb60b 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -57,6 +57,19 @@ service Control { // Delete the egress policy resource nested under an Actor. rpc DeleteActorEgressPolicy(DeleteActorEgressPolicyRequest) returns (EgressPolicy) {} + // Create a Substrate-issued JWT asserting the actor identity. + // + // * Called by the egress gateway when actor JWT injection is configured for outbound requests. + rpc MintActorJWT(MintActorJWTRequest) returns (MintActorJWTResponse) {} + + // Create a Substrate-issued SPIFFE certificate asserting the actor identity. + // + // * Called by atelet to provision an atunnel with a certificate for + // communication with the egress gateway. TODO(ahmedtd): Migrate this use + // case to a distinct certificate to prevent actor/atunnel confusion. + // * Called by the egress gateway when actor client certificate injection is + // configured for outbound requests. + rpc MintActorCertificate(MintActorCertificateRequest) returns (MintActorCertificateResponse) {} // Get an ActorSnapshot. rpc GetActorSnapshot(GetActorSnapshotRequest) returns (ActorSnapshot) {} @@ -1411,6 +1424,96 @@ message DeleteActorEgressPolicyRequest { ObjectRef actor = 1; } +message MintActorJWTRequest { + // The actor for which the JWT should be issued. + // + // Must be a valid actor that currently exists according to the actor store. + // + // +k8s:required + ObjectRef actor = 5; + + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + // + // +k8s:required + // +k8s:format=k8s-uuid + string actor_uid = 7; + + // The audiences the minted JWT is bound to. Tokens are only issued with + // audience bindings, so at least one is required. + // + // +k8s:required + // +k8s:maxItems=16 # guardrail; tokens realistically bind a handful of audiences + // +k8s:listType=set + // +k8s:eachVal=+k8s:maxLength=512 # audiences are caller-defined URIs; bound only + repeated string audience = 1; +} + +// TODO: check why k8s do ":" and not "/" as a seprator for the Subject format +// TODO: whats the right format for the subject? kubernetes follow "system:serviceaccount::". +message MintActorJWTResponse { + // Actor JWT. An OIDC Discovery-compatible JWT + // + // Claims: + // + // * iss: Issuer - a valid URL where a relying party can fetch the OIDC + // discovery documents. + // * sub: Subject - a string expressing the identity carried in the + // credential. Format + // `atespaces:${atespace}:actors:${actorname}`. + // * aud: Audience - a string identifying the service this token will be used + // to authenticate to. + // * nbf: Not Before - a numeric unix timestamp + // * exp: Expiration - a numeric unix timestamp + // * iat: Issued At - a numeric unix timestamp + // * `ate.dev`: Ate/Substrate Extension - JSON object + // * atespace: (string) The atespace the actor belongs to + // * actorName: (string) The actor's name, unique within its atespace + string actor_jwt = 1; +} + +message MintActorCertificateRequest { + // The actor for which the certificate should be issued. + // + // Must be a valid actor that currently exists according to the actor store. + // + // +k8s:required + ObjectRef actor = 6; + + // The UID of the actor --- used to guard against deletion and recreation of + // an actor with the same name. + // + // +k8s:required + // +k8s:format=k8s-uuid + string actor_uid = 7; + + // Request contains DER encoded bytes of a x509 certificate signing request. + // The signer will ignore the contents of the CSR except to extract the + // subject public key. + // + // +k8s:required + // +k8s:customValidation # size bound; maxLength is string-only + bytes certificate_signing_request = 2; + + // +k8s:required + // +k8s:minimum=1 + // +k8s:maximum=1 # keep this in sync with the ActorCertificatePurpose enum + ActorCertificatePurpose purpose = 4; +} + +enum ActorCertificatePurpose { + ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED = 0; + ACTOR_CERTIFICATE_PURPOSE_ATUNNEL = 1; + // Keep this in sync with MintCertRequest.purpose's maximum. +} + +message MintActorCertificateResponse { + // Response contains a list of DER encoded certificates. The first entry is the + // leaf certificate, and any remaining entries are intermediates in + // leaf-to-root order. + repeated bytes actor_certificates = 1; +} + message GetActorSnapshotRequest { // +k8s:opaqueType ObjectRef actor_snapshot = 1; @@ -1732,124 +1835,3 @@ message ActorAssignment { // +k8s:subfield(atespace)=+k8s:required ObjectRef actor_template_ref = 4; } - -// ActorIdentity allows substrate workloads to exchange their -// infrastructure-level credentials (k8s service account token, etc.) for a -// substrate actor-level credential. A given substrate actor might migrate -// between many different physical workers over the course of its lifecycle, -// whereas the actor credential's identity will be stable for the life of the -// actor. -service ActorIdentity { - // Request an Actor Identity JWT. - // - // To call this RPC, you must be authenticated as the Kubernetes Pod that is - // currently running the requested actor. - rpc MintJWT(MintJWTRequest) returns (MintJWTResponse); - - // Request an Actor Identity Certificate for an actor. - // - // Actors do not call this RPC themselves. The atelet hosting the actor calls - // it on the actor's behalf, authenticating with its own client certificate - // rather than a bearer token. - // - // Authorization is decided on that client certificate and the worker - // identity attested by atelet. Ateapi verifies that the worker is assigned to - // the actor and that the actor points back to that exact worker before signing. - // - // The certificate in the response is the actor's identity, not the atelet's. - rpc MintCert(MintCertRequest) returns (MintCertResponse); -} - -message MintJWTRequest { - // The audiences the minted JWT is bound to. Tokens are only issued with - // audience bindings, so at least one is required. - // - // +k8s:required - // +k8s:maxItems=16 # guardrail; tokens realistically bind a handful of audiences - // +k8s:listType=set - // +k8s:eachVal=+k8s:maxLength=512 # audiences are caller-defined URIs; bound only - repeated string audience = 1; - - // +k8s:required - // +k8s:format=k8s-short-name - string atespace = 2; - - // +k8s:required - // +k8s:format=k8s-short-name - string actor_name = 3; - - // +k8s:optional - // +k8s:format=k8s-uuid - string actor_uid = 4; -} - -// TODO: check why k8s do ":" and not "/" as a seprator for the Subject format -// TODO: whats the right format for the subject? kubernetes follow "system:serviceaccount::". -message MintJWTResponse { - // Actor JWT. An OIDC Discovery-compatible JWT - // - // Claims: - // - // * iss: Issuer - a valid URL where a relying party can fetch the OIDC - // discovery documents. - // * sub: Subject - a string expressing the identity carried in the - // credential. Format - // `atespaces:${atespace}:actors:${actorname}`. - // * aud: Audience - a string identifying the service this token will be used - // to authenticate to. - // * nbf: Not Before - a numeric unix timestamp - // * exp: Expiration - a numeric unix timestamp - // * iat: Issued At - a numeric unix timestamp - // * `ate.dev`: Ate/Substrate Extension - JSON object - // * atespace: (string) The atespace the actor belongs to - // * actorName: (string) The actor's name, unique within its atespace - string actor_jwt = 1; -} - -message MintCertRequest { - // The Worker the certificate is minted for, as authenticated by the - // node-local atelet. Workers are global-scoped, so this carries no atespace. - // Ateapi resolves the worker's current actor assignment rather than trusting - // actor metadata from the caller. - // - // This is the one caller that recovers a Worker name from a pod certificate: - // the atelet has only the worker Pod's identity to go on. Everywhere else the - // name is opaque and must be carried, not reconstructed. - // - // +k8s:beta(since: "0.0")=+k8s:subfield(atespace)=+k8s:forbidden # TODO: get rid of beta prefix - // +k8s:required - ObjectRef worker = 1; - - // Request contains DER encoded bytes of a x509 certificate signing request. - // The signer will ignore the contents of the CSR except to extract the - // subject public key. - // - // +k8s:required - // +k8s:customValidation # size bound; maxLength is string-only - bytes certificate_signing_request = 2; - - // Actor incarnation expected by the activation. This is only a stale-request - // guard: ateapi derives the actor and its identity from the worker assignment. - // - // +k8s:required - // +k8s:format=k8s-uuid - string expected_actor_uid = 3; - - // +k8s:required - // +k8s:minimum=1 - // +k8s:maximum=1 # keep this in sync with the ActorCertificatePurpose enum - ActorCertificatePurpose purpose = 4; -} - -enum ActorCertificatePurpose { - ACTOR_CERTIFICATE_PURPOSE_UNSPECIFIED = 0; - ACTOR_CERTIFICATE_PURPOSE_ATUNNEL = 1; - // Keep this in sync with MintCertRequest.purpose's maximum. -} - -message MintCertResponse { - // Response contains a list of DER encoded certificates. The first entry is the - // leaf certificate, and any remaining entries are intermediates in - // leaf-to-root order. - repeated bytes actor_certificates = 1; -} diff --git a/pkg/proto/ateapipb/ateapi_grpc.pb.go b/pkg/proto/ateapipb/ateapi_grpc.pb.go index bafed77e1f..b4be78c336 100644 --- a/pkg/proto/ateapipb/ateapi_grpc.pb.go +++ b/pkg/proto/ateapipb/ateapi_grpc.pb.go @@ -44,6 +44,8 @@ const ( Control_CreateActorEgressPolicy_FullMethodName = "/ateapi.Control/CreateActorEgressPolicy" Control_UpdateActorEgressPolicy_FullMethodName = "/ateapi.Control/UpdateActorEgressPolicy" Control_DeleteActorEgressPolicy_FullMethodName = "/ateapi.Control/DeleteActorEgressPolicy" + Control_MintActorJWT_FullMethodName = "/ateapi.Control/MintActorJWT" + Control_MintActorCertificate_FullMethodName = "/ateapi.Control/MintActorCertificate" Control_GetActorSnapshot_FullMethodName = "/ateapi.Control/GetActorSnapshot" Control_GetActorSnapshotTag_FullMethodName = "/ateapi.Control/GetActorSnapshotTag" Control_ListActorSnapshots_FullMethodName = "/ateapi.Control/ListActorSnapshots" @@ -97,6 +99,18 @@ type ControlClient interface { UpdateActorEgressPolicy(ctx context.Context, in *UpdateActorEgressPolicyRequest, opts ...grpc.CallOption) (*EgressPolicy, error) // Delete the egress policy resource nested under an Actor. DeleteActorEgressPolicy(ctx context.Context, in *DeleteActorEgressPolicyRequest, opts ...grpc.CallOption) (*EgressPolicy, error) + // Create a Substrate-issued JWT asserting the actor identity. + // + // * Called by the egress gateway when actor JWT injection is configured for outbound requests. + MintActorJWT(ctx context.Context, in *MintActorJWTRequest, opts ...grpc.CallOption) (*MintActorJWTResponse, error) + // Create a Substrate-issued SPIFFE certificate asserting the actor identity. + // + // * Called by atelet to provision an atunnel with a certificate for + // communication with the egress gateway. TODO(ahmedtd): Migrate this use + // case to a distinct certificate to prevent actor/atunnel confusion. + // * Called by the egress gateway when actor client certificate injection is + // configured for outbound requests. + MintActorCertificate(ctx context.Context, in *MintActorCertificateRequest, opts ...grpc.CallOption) (*MintActorCertificateResponse, error) // Get an ActorSnapshot. GetActorSnapshot(ctx context.Context, in *GetActorSnapshotRequest, opts ...grpc.CallOption) (*ActorSnapshot, error) // Get an ActorSnapshot tag. @@ -262,6 +276,26 @@ func (c *controlClient) DeleteActorEgressPolicy(ctx context.Context, in *DeleteA return out, nil } +func (c *controlClient) MintActorJWT(ctx context.Context, in *MintActorJWTRequest, opts ...grpc.CallOption) (*MintActorJWTResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MintActorJWTResponse) + err := c.cc.Invoke(ctx, Control_MintActorJWT_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) MintActorCertificate(ctx context.Context, in *MintActorCertificateRequest, opts ...grpc.CallOption) (*MintActorCertificateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MintActorCertificateResponse) + err := c.cc.Invoke(ctx, Control_MintActorCertificate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *controlClient) GetActorSnapshot(ctx context.Context, in *GetActorSnapshotRequest, opts ...grpc.CallOption) (*ActorSnapshot, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ActorSnapshot) @@ -502,6 +536,18 @@ type ControlServer interface { UpdateActorEgressPolicy(context.Context, *UpdateActorEgressPolicyRequest) (*EgressPolicy, error) // Delete the egress policy resource nested under an Actor. DeleteActorEgressPolicy(context.Context, *DeleteActorEgressPolicyRequest) (*EgressPolicy, error) + // Create a Substrate-issued JWT asserting the actor identity. + // + // * Called by the egress gateway when actor JWT injection is configured for outbound requests. + MintActorJWT(context.Context, *MintActorJWTRequest) (*MintActorJWTResponse, error) + // Create a Substrate-issued SPIFFE certificate asserting the actor identity. + // + // * Called by atelet to provision an atunnel with a certificate for + // communication with the egress gateway. TODO(ahmedtd): Migrate this use + // case to a distinct certificate to prevent actor/atunnel confusion. + // * Called by the egress gateway when actor client certificate injection is + // configured for outbound requests. + MintActorCertificate(context.Context, *MintActorCertificateRequest) (*MintActorCertificateResponse, error) // Get an ActorSnapshot. GetActorSnapshot(context.Context, *GetActorSnapshotRequest) (*ActorSnapshot, error) // Get an ActorSnapshot tag. @@ -590,6 +636,12 @@ func (UnimplementedControlServer) UpdateActorEgressPolicy(context.Context, *Upda func (UnimplementedControlServer) DeleteActorEgressPolicy(context.Context, *DeleteActorEgressPolicyRequest) (*EgressPolicy, error) { return nil, status.Error(codes.Unimplemented, "method DeleteActorEgressPolicy not implemented") } +func (UnimplementedControlServer) MintActorJWT(context.Context, *MintActorJWTRequest) (*MintActorJWTResponse, error) { + return nil, status.Error(codes.Unimplemented, "method MintActorJWT not implemented") +} +func (UnimplementedControlServer) MintActorCertificate(context.Context, *MintActorCertificateRequest) (*MintActorCertificateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method MintActorCertificate not implemented") +} func (UnimplementedControlServer) GetActorSnapshot(context.Context, *GetActorSnapshotRequest) (*ActorSnapshot, error) { return nil, status.Error(codes.Unimplemented, "method GetActorSnapshot not implemented") } @@ -872,6 +924,42 @@ func _Control_DeleteActorEgressPolicy_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _Control_MintActorJWT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MintActorJWTRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).MintActorJWT(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_MintActorJWT_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).MintActorJWT(ctx, req.(*MintActorJWTRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_MintActorCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MintActorCertificateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).MintActorCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_MintActorCertificate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).MintActorCertificate(ctx, req.(*MintActorCertificateRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Control_GetActorSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetActorSnapshotRequest) if err := dec(in); err != nil { @@ -1301,6 +1389,14 @@ var Control_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteActorEgressPolicy", Handler: _Control_DeleteActorEgressPolicy_Handler, }, + { + MethodName: "MintActorJWT", + Handler: _Control_MintActorJWT_Handler, + }, + { + MethodName: "MintActorCertificate", + Handler: _Control_MintActorCertificate_Handler, + }, { MethodName: "GetActorSnapshot", Handler: _Control_GetActorSnapshot_Handler, @@ -1389,187 +1485,3 @@ var Control_ServiceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "ateapi.proto", } - -const ( - ActorIdentity_MintJWT_FullMethodName = "/ateapi.ActorIdentity/MintJWT" - ActorIdentity_MintCert_FullMethodName = "/ateapi.ActorIdentity/MintCert" -) - -// ActorIdentityClient is the client API for ActorIdentity service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// ActorIdentity allows substrate workloads to exchange their -// infrastructure-level credentials (k8s service account token, etc.) for a -// substrate actor-level credential. A given substrate actor might migrate -// between many different physical workers over the course of its lifecycle, -// whereas the actor credential's identity will be stable for the life of the -// actor. -type ActorIdentityClient interface { - // Request an Actor Identity JWT. - // - // To call this RPC, you must be authenticated as the Kubernetes Pod that is - // currently running the requested actor. - MintJWT(ctx context.Context, in *MintJWTRequest, opts ...grpc.CallOption) (*MintJWTResponse, error) - // Request an Actor Identity Certificate for an actor. - // - // Actors do not call this RPC themselves. The atelet hosting the actor calls - // it on the actor's behalf, authenticating with its own client certificate - // rather than a bearer token. - // - // Authorization is decided on that client certificate and the worker - // identity attested by atelet. Ateapi verifies that the worker is assigned to - // the actor and that the actor points back to that exact worker before signing. - // - // The certificate in the response is the actor's identity, not the atelet's. - MintCert(ctx context.Context, in *MintCertRequest, opts ...grpc.CallOption) (*MintCertResponse, error) -} - -type actorIdentityClient struct { - cc grpc.ClientConnInterface -} - -func NewActorIdentityClient(cc grpc.ClientConnInterface) ActorIdentityClient { - return &actorIdentityClient{cc} -} - -func (c *actorIdentityClient) MintJWT(ctx context.Context, in *MintJWTRequest, opts ...grpc.CallOption) (*MintJWTResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(MintJWTResponse) - err := c.cc.Invoke(ctx, ActorIdentity_MintJWT_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *actorIdentityClient) MintCert(ctx context.Context, in *MintCertRequest, opts ...grpc.CallOption) (*MintCertResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(MintCertResponse) - err := c.cc.Invoke(ctx, ActorIdentity_MintCert_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ActorIdentityServer is the server API for ActorIdentity service. -// All implementations must embed UnimplementedActorIdentityServer -// for forward compatibility. -// -// ActorIdentity allows substrate workloads to exchange their -// infrastructure-level credentials (k8s service account token, etc.) for a -// substrate actor-level credential. A given substrate actor might migrate -// between many different physical workers over the course of its lifecycle, -// whereas the actor credential's identity will be stable for the life of the -// actor. -type ActorIdentityServer interface { - // Request an Actor Identity JWT. - // - // To call this RPC, you must be authenticated as the Kubernetes Pod that is - // currently running the requested actor. - MintJWT(context.Context, *MintJWTRequest) (*MintJWTResponse, error) - // Request an Actor Identity Certificate for an actor. - // - // Actors do not call this RPC themselves. The atelet hosting the actor calls - // it on the actor's behalf, authenticating with its own client certificate - // rather than a bearer token. - // - // Authorization is decided on that client certificate and the worker - // identity attested by atelet. Ateapi verifies that the worker is assigned to - // the actor and that the actor points back to that exact worker before signing. - // - // The certificate in the response is the actor's identity, not the atelet's. - MintCert(context.Context, *MintCertRequest) (*MintCertResponse, error) - mustEmbedUnimplementedActorIdentityServer() -} - -// UnimplementedActorIdentityServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedActorIdentityServer struct{} - -func (UnimplementedActorIdentityServer) MintJWT(context.Context, *MintJWTRequest) (*MintJWTResponse, error) { - return nil, status.Error(codes.Unimplemented, "method MintJWT not implemented") -} -func (UnimplementedActorIdentityServer) MintCert(context.Context, *MintCertRequest) (*MintCertResponse, error) { - return nil, status.Error(codes.Unimplemented, "method MintCert not implemented") -} -func (UnimplementedActorIdentityServer) mustEmbedUnimplementedActorIdentityServer() {} -func (UnimplementedActorIdentityServer) testEmbeddedByValue() {} - -// UnsafeActorIdentityServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ActorIdentityServer will -// result in compilation errors. -type UnsafeActorIdentityServer interface { - mustEmbedUnimplementedActorIdentityServer() -} - -func RegisterActorIdentityServer(s grpc.ServiceRegistrar, srv ActorIdentityServer) { - // If the following call panics, it indicates UnimplementedActorIdentityServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&ActorIdentity_ServiceDesc, srv) -} - -func _ActorIdentity_MintJWT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MintJWTRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ActorIdentityServer).MintJWT(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ActorIdentity_MintJWT_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ActorIdentityServer).MintJWT(ctx, req.(*MintJWTRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ActorIdentity_MintCert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MintCertRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ActorIdentityServer).MintCert(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ActorIdentity_MintCert_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ActorIdentityServer).MintCert(ctx, req.(*MintCertRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// ActorIdentity_ServiceDesc is the grpc.ServiceDesc for ActorIdentity service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ActorIdentity_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "ateapi.ActorIdentity", - HandlerType: (*ActorIdentityServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "MintJWT", - Handler: _ActorIdentity_MintJWT_Handler, - }, - { - MethodName: "MintCert", - Handler: _ActorIdentity_MintCert_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "ateapi.proto", -}