Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ defaultPlatforms:
baseImageOverrides:
github.com/agent-substrate/substrate/demos/sandbox: alpine
github.com/agent-substrate/substrate/demos/agent-secret: alpine

x-agentgatewayEgressBaseImageOverrides:
github.com/agent-substrate/substrate/cmd/ateom-gvisor: cr.agentgateway.dev/agentgateway:latest-dev
323 changes: 323 additions & 0 deletions charts/substrate-crds/templates/ate.dev_actortemplates.yaml

Large diffs are not rendered by default.

25 changes: 2 additions & 23 deletions cmd/ateapi/internal/controlapi/workflow_suspend.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,29 +141,8 @@ func (s *CallAteletSuspendStep) Execute(ctx context.Context, input *SuspendInput
ActorTemplateName: state.Actor.GetActorTemplateName(),
ActorId: state.Actor.GetActorId(),
Runsc: runscCfg,
Spec: &ateletpb.WorkloadSpec{
PauseImage: state.ActorTemplate.Spec.PauseImage,
},
SnapshotUriPrefix: state.Actor.GetInProgressSnapshot(),
}
for _, ctr := range state.ActorTemplate.Spec.Containers {
ateletCtr := &ateletpb.Container{
Name: ctr.Name,
Image: ctr.Image,
Command: ctr.Command,
}
for _, env := range ctr.Env {
var val string
if env.Value != nil {
val = *env.Value
}
ateletEnv := &ateletpb.EnvEntry{
Name: env.Name,
Value: val,
}
ateletCtr.Env = append(ateletCtr.Env, ateletEnv)
}
req.Spec.Containers = append(req.Spec.Containers, ateletCtr)
Spec: checkpointWorkloadSpecFromActorTemplate(state.ActorTemplate),
SnapshotUriPrefix: state.Actor.GetInProgressSnapshot(),
}
_, err = client.Checkpoint(ctx, req)
if err != nil {
Expand Down
120 changes: 119 additions & 1 deletion cmd/ateapi/internal/controlapi/workload_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const envSecretCacheTTL = 30 * time.Second

func workloadSpecFromActorTemplate(ctx context.Context, kubeClient kubernetes.Interface, secretCache *envSecretCache, actorTemplate *atev1alpha1.ActorTemplate) (*ateletpb.WorkloadSpec, error) {
workloadSpec := &ateletpb.WorkloadSpec{
PauseImage: actorTemplate.Spec.PauseImage,
PauseImage: actorTemplate.Spec.PauseImage,
EgressPolicy: buildAteletEgressPolicy(actorTemplate.Spec.EgressPolicy),
}
resolver := envResolver{
kubeClient: kubeClient,
Expand Down Expand Up @@ -63,6 +64,123 @@ func workloadSpecFromActorTemplate(ctx context.Context, kubeClient kubernetes.In
return workloadSpec, nil
}

func checkpointWorkloadSpecFromActorTemplate(actorTemplate *atev1alpha1.ActorTemplate) *ateletpb.WorkloadSpec {
workloadSpec := &ateletpb.WorkloadSpec{
PauseImage: actorTemplate.Spec.PauseImage,
EgressPolicy: buildAteletEgressPolicy(actorTemplate.Spec.EgressPolicy),
}
for _, ctr := range actorTemplate.Spec.Containers {
ateletCtr := &ateletpb.Container{
Name: ctr.Name,
Image: ctr.Image,
Command: ctr.Command,
}
for _, env := range ctr.Env {
var val string
if env.Value != nil {
val = *env.Value
}
ateletCtr.Env = append(ateletCtr.Env, &ateletpb.EnvEntry{
Name: env.Name,
Value: val,
})
}
workloadSpec.Containers = append(workloadSpec.Containers, ateletCtr)
}
return workloadSpec
}

func buildAteletEgressPolicy(policy *atev1alpha1.EgressPolicy) *ateletpb.EgressPolicy {
if policy == nil {
return nil
}
return &ateletpb.EgressPolicy{
DefaultAction: string(policy.DefaultAction),
Allow: buildAteletEgressPolicyRules(policy.Allow),
Deny: buildAteletEgressPolicyRules(policy.Deny),
Audit: buildAteletEgressAuditPolicy(policy.Audit),
}
}

func buildAteletEgressAuditPolicy(policy *atev1alpha1.EgressAuditPolicy) *ateletpb.EgressAuditPolicy {
if policy == nil {
return nil
}
return &ateletpb.EgressAuditPolicy{
Logs: policy.Logs,
Traces: policy.Traces,
RedactHeaders: append([]string(nil), policy.RedactHeaders...),
}
}

func buildAteletEgressPolicyRules(rules []atev1alpha1.EgressPolicyRule) []*ateletpb.EgressPolicyRule {
out := make([]*ateletpb.EgressPolicyRule, 0, len(rules))
for _, rule := range rules {
outRule := &ateletpb.EgressPolicyRule{}
for _, dest := range rule.To {
outDest := &ateletpb.EgressPolicyDestination{Host: dest.Host}
if dest.IPBlock != nil {
outDest.Cidr = dest.IPBlock.CIDR
}
outRule.To = append(outRule.To, outDest)
}
for _, port := range rule.Ports {
outRule.Ports = append(outRule.Ports, &ateletpb.EgressPort{
Port: uint32(port.Port),
Protocol: string(port.Protocol),
})
}
outRule.Tls = buildAteletEgressTLSPolicy(rule.TLS)
outRule.Credentials = buildAteletEgressCredentialPolicy(rule.Credentials)
out = append(out, outRule)
}
return out
}

func buildAteletEgressTLSPolicy(policy *atev1alpha1.EgressTLSPolicy) *ateletpb.EgressTLSPolicy {
if policy == nil {
return nil
}
out := &ateletpb.EgressTLSPolicy{
Mode: string(policy.Mode),
Required: policy.Required,
}
if policy.Intercept != nil {
out.Intercept = &ateletpb.EgressTLSInterceptPolicy{
ValidateUpstream: policy.Intercept.ValidateUpstream,
}
if policy.Intercept.IssuerSecretRef != nil {
out.Intercept.IssuerSecretRef = &ateletpb.SecretReference{
Name: policy.Intercept.IssuerSecretRef.Name,
Namespace: policy.Intercept.IssuerSecretRef.Namespace,
}
}
}
return out
}

func buildAteletEgressCredentialPolicy(policy *atev1alpha1.EgressCredentialPolicy) *ateletpb.EgressCredentialPolicy {
if policy == nil {
return nil
}
out := &ateletpb.EgressCredentialPolicy{}
for _, injection := range policy.Inject {
outInjection := &ateletpb.EgressCredentialInjection{
Header: injection.Header,
}
if injection.ValueFrom.SecretKeyRef != nil {
outInjection.ValueFrom = &ateletpb.EgressCredentialValueFrom{
SecretKeyRef: &ateletpb.SecretKeySelector{
Name: injection.ValueFrom.SecretKeyRef.Name,
Key: injection.ValueFrom.SecretKeyRef.Key,
},
}
}
out.Inject = append(out.Inject, outInjection)
}
return out
}

type envResolver struct {
kubeClient kubernetes.Interface
namespace string
Expand Down
95 changes: 94 additions & 1 deletion cmd/atelet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,13 +587,106 @@ func (s *AteomHerder) dialAteom(ctx context.Context, targetAteomUid string) (ate
// buildAteomWorkloadSpec projects the atelet-facing workload spec onto
// the ateom-facing one — currently just the container names.
func buildAteomWorkloadSpec(spec *ateletpb.WorkloadSpec) *ateompb.WorkloadSpec {
out := &ateompb.WorkloadSpec{}
out := &ateompb.WorkloadSpec{
EgressPolicy: buildAteomEgressPolicy(spec.GetEgressPolicy()),
}
for _, ctr := range spec.GetContainers() {
out.Containers = append(out.Containers, &ateompb.Container{Name: ctr.GetName()})
}
return out
}

func buildAteomEgressPolicy(policy *ateletpb.EgressPolicy) *ateompb.EgressPolicy {
if policy == nil {
return nil
}
return &ateompb.EgressPolicy{
DefaultAction: policy.GetDefaultAction(),
Allow: buildAteomEgressPolicyRules(policy.GetAllow()),
Deny: buildAteomEgressPolicyRules(policy.GetDeny()),
Audit: buildAteomEgressAuditPolicy(policy.GetAudit()),
}
}

func buildAteomEgressAuditPolicy(policy *ateletpb.EgressAuditPolicy) *ateompb.EgressAuditPolicy {
if policy == nil {
return nil
}
return &ateompb.EgressAuditPolicy{
Logs: policy.GetLogs(),
Traces: policy.GetTraces(),
RedactHeaders: append([]string(nil), policy.GetRedactHeaders()...),
}
}

func buildAteomEgressPolicyRules(rules []*ateletpb.EgressPolicyRule) []*ateompb.EgressPolicyRule {
out := make([]*ateompb.EgressPolicyRule, 0, len(rules))
for _, rule := range rules {
outRule := &ateompb.EgressPolicyRule{
Tls: buildAteomEgressTLSPolicy(rule.GetTls()),
Credentials: buildAteomEgressCredentialPolicy(rule.GetCredentials()),
}
for _, dest := range rule.GetTo() {
outRule.To = append(outRule.To, &ateompb.EgressPolicyDestination{
Host: dest.GetHost(),
Cidr: dest.GetCidr(),
})
}
for _, port := range rule.GetPorts() {
outRule.Ports = append(outRule.Ports, &ateompb.EgressPort{
Port: port.GetPort(),
Protocol: port.GetProtocol(),
})
}
out = append(out, outRule)
}
return out
}

func buildAteomEgressTLSPolicy(policy *ateletpb.EgressTLSPolicy) *ateompb.EgressTLSPolicy {
if policy == nil {
return nil
}
out := &ateompb.EgressTLSPolicy{
Mode: policy.GetMode(),
Required: policy.GetRequired(),
}
if policy.GetIntercept() != nil {
out.Intercept = &ateompb.EgressTLSInterceptPolicy{
ValidateUpstream: policy.GetIntercept().GetValidateUpstream(),
}
if policy.GetIntercept().GetIssuerSecretRef() != nil {
out.Intercept.IssuerSecretRef = &ateompb.SecretReference{
Name: policy.GetIntercept().GetIssuerSecretRef().GetName(),
Namespace: policy.GetIntercept().GetIssuerSecretRef().GetNamespace(),
}
}
}
return out
}

func buildAteomEgressCredentialPolicy(policy *ateletpb.EgressCredentialPolicy) *ateompb.EgressCredentialPolicy {
if policy == nil {
return nil
}
out := &ateompb.EgressCredentialPolicy{}
for _, injection := range policy.GetInject() {
outInjection := &ateompb.EgressCredentialInjection{
Header: injection.GetHeader(),
}
if injection.GetValueFrom().GetSecretKeyRef() != nil {
outInjection.ValueFrom = &ateompb.EgressCredentialValueFrom{
SecretKeyRef: &ateompb.SecretKeySelector{
Name: injection.GetValueFrom().GetSecretKeyRef().GetName(),
Key: injection.GetValueFrom().GetSecretKeyRef().GetKey(),
},
}
}
out.Inject = append(out.Inject, outInjection)
}
return out
}

// uploadIfExists uploads a local file to GCS (zstd-compressed) only if
// the file is present. Missing files are silently skipped — used for
// optional checkpoint side-files (pages.img, pages_meta.img).
Expand Down
Loading
Loading