Unverified Commit 1e8b2866 authored by hhzhang16's avatar hhzhang16 Committed by GitHub
Browse files

feat: add ingress to graph deployments (#960)

parent a590d103
...@@ -36,6 +36,7 @@ export INGRESS_ENABLED="${INGRESS_ENABLED:=false}" ...@@ -36,6 +36,7 @@ export INGRESS_ENABLED="${INGRESS_ENABLED:=false}"
export ISTIO_ENABLED="${ISTIO_ENABLED:=false}" export ISTIO_ENABLED="${ISTIO_ENABLED:=false}"
export ISTIO_GATEWAY="${ISTIO_GATEWAY:=istio-system/istio-ingressgateway}" export ISTIO_GATEWAY="${ISTIO_GATEWAY:=istio-system/istio-ingressgateway}"
export INGRESS_CLASS="${INGRESS_CLASS:=nginx}" export INGRESS_CLASS="${INGRESS_CLASS:=nginx}"
export VIRTUAL_SERVICE_SUPPORTS_HTTPS="${VIRTUAL_SERVICE_SUPPORTS_HTTPS:=false}"
# Add command line options # Add command line options
INTERACTIVE=false INTERACTIVE=false
...@@ -140,8 +141,9 @@ echo "ISTIO_ENABLED: $ISTIO_ENABLED" ...@@ -140,8 +141,9 @@ echo "ISTIO_ENABLED: $ISTIO_ENABLED"
echo "INGRESS_CLASS: $INGRESS_CLASS" echo "INGRESS_CLASS: $INGRESS_CLASS"
echo "ISTIO_GATEWAY: $ISTIO_GATEWAY" echo "ISTIO_GATEWAY: $ISTIO_GATEWAY"
echo "DYNAMO_INGRESS_SUFFIX: $DYNAMO_INGRESS_SUFFIX" echo "DYNAMO_INGRESS_SUFFIX: $DYNAMO_INGRESS_SUFFIX"
echo "VIRTUAL_SERVICE_SUPPORTS_HTTPS: $VIRTUAL_SERVICE_SUPPORTS_HTTPS"
envsubst '${NAMESPACE} ${RELEASE_NAME} ${DOCKER_USERNAME} ${DOCKER_PASSWORD} ${DOCKER_SERVER} ${IMAGE_TAG} ${DYNAMO_INGRESS_SUFFIX} ${PIPELINES_DOCKER_SERVER} ${PIPELINES_DOCKER_USERNAME} ${PIPELINES_DOCKER_PASSWORD} ${DOCKER_SECRET_NAME} ${INGRESS_ENABLED} ${ISTIO_ENABLED} ${INGRESS_CLASS} ${ISTIO_GATEWAY}' < dynamo-platform-values.yaml > generated-values.yaml envsubst '${NAMESPACE} ${RELEASE_NAME} ${DOCKER_USERNAME} ${DOCKER_PASSWORD} ${DOCKER_SERVER} ${IMAGE_TAG} ${DYNAMO_INGRESS_SUFFIX} ${PIPELINES_DOCKER_SERVER} ${PIPELINES_DOCKER_USERNAME} ${PIPELINES_DOCKER_PASSWORD} ${DOCKER_SECRET_NAME} ${INGRESS_ENABLED} ${ISTIO_ENABLED} ${INGRESS_CLASS} ${ISTIO_GATEWAY} ${VIRTUAL_SERVICE_SUPPORTS_HTTPS}' < dynamo-platform-values.yaml > generated-values.yaml
echo "generated file contents:" echo "generated file contents:"
cat generated-values.yaml cat generated-values.yaml
......
...@@ -40,6 +40,7 @@ dynamo-operator: ...@@ -40,6 +40,7 @@ dynamo-operator:
username: ${PIPELINES_DOCKER_USERNAME} username: ${PIPELINES_DOCKER_USERNAME}
password: ${PIPELINES_DOCKER_PASSWORD} password: ${PIPELINES_DOCKER_PASSWORD}
imageBuildEngine: buildkit imageBuildEngine: buildkit
virtualServiceSupportsHTTPS: ${VIRTUAL_SERVICE_SUPPORTS_HTTPS}
dynamo-api-store: dynamo-api-store:
namespaceRestriction: namespaceRestriction:
......
...@@ -253,6 +253,14 @@ if [ "$CONFIG_TYPE" = "istio" ]; then ...@@ -253,6 +253,14 @@ if [ "$CONFIG_TYPE" = "istio" ]; then
fi fi
fi fi
# Ask if the Istio gateway supports HTTPS
read -p "Does your Istio gateway support HTTPS? (y/n): " SUPPORTS_HTTPS_REPLY
if [[ "$SUPPORTS_HTTPS_REPLY" =~ ^[Yy]$ ]]; then
export VIRTUAL_SERVICE_SUPPORTS_HTTPS=true
else
export VIRTUAL_SERVICE_SUPPORTS_HTTPS=false
fi
elif [ "$CONFIG_TYPE" = "ingress" ]; then elif [ "$CONFIG_TYPE" = "ingress" ]; then
echo -e "${CYAN}Configuring Ingress settings...${NC}" echo -e "${CYAN}Configuring Ingress settings...${NC}"
...@@ -332,4 +340,4 @@ elif [ "$CONFIG_TYPE" = "ingress" ]; then ...@@ -332,4 +340,4 @@ elif [ "$CONFIG_TYPE" = "ingress" ]; then
fi fi
print_header "Wizard Complete" print_header "Wizard Complete"
echo -e "${GREEN}Network configuration complete!${NC}" echo -e "${GREEN}Network configuration complete!${NC}"
\ No newline at end of file
...@@ -86,6 +86,9 @@ spec: ...@@ -86,6 +86,9 @@ spec:
{{- if .Values.dynamo.ingressHostSuffix }} {{- if .Values.dynamo.ingressHostSuffix }}
- --ingress-host-suffix={{ .Values.dynamo.ingressHostSuffix }} - --ingress-host-suffix={{ .Values.dynamo.ingressHostSuffix }}
{{- end }} {{- end }}
{{- if .Values.dynamo.virtualServiceSupportsHTTPS }}
- --virtual-service-supports-https={{ .Values.dynamo.virtualServiceSupportsHTTPS }}
{{- end }}
command: command:
- /manager - /manager
......
...@@ -71,6 +71,7 @@ func main() { ...@@ -71,6 +71,7 @@ func main() {
var natsAddr string var natsAddr string
var etcdAddr string var etcdAddr string
var istioVirtualServiceGateway string var istioVirtualServiceGateway string
var virtualServiceSupportsHTTPS bool
var ingressControllerClassName string var ingressControllerClassName string
var ingressControllerTLSSecretName string var ingressControllerTLSSecretName string
var ingressHostSuffix string var ingressHostSuffix string
...@@ -91,6 +92,8 @@ func main() { ...@@ -91,6 +92,8 @@ func main() {
flag.StringVar(&etcdAddr, "etcdAddr", "", "address of the etcd server") flag.StringVar(&etcdAddr, "etcdAddr", "", "address of the etcd server")
flag.StringVar(&istioVirtualServiceGateway, "istio-virtual-service-gateway", "", flag.StringVar(&istioVirtualServiceGateway, "istio-virtual-service-gateway", "",
"The name of the istio virtual service gateway to use") "The name of the istio virtual service gateway to use")
flag.BoolVar(&virtualServiceSupportsHTTPS, "virtual-service-supports-https", false,
"If set, assume VirtualService endpoints are HTTPS")
flag.StringVar(&ingressControllerClassName, "ingress-controller-class-name", "", flag.StringVar(&ingressControllerClassName, "ingress-controller-class-name", "",
"The name of the ingress controller class to use") "The name of the ingress controller class to use")
flag.StringVar(&ingressControllerTLSSecretName, "ingress-controller-tls-secret-name", "", flag.StringVar(&ingressControllerTLSSecretName, "ingress-controller-tls-secret-name", "",
...@@ -106,7 +109,8 @@ func main() { ...@@ -106,7 +109,8 @@ func main() {
utilruntime.Must(istioclientsetscheme.AddToScheme(scheme)) utilruntime.Must(istioclientsetscheme.AddToScheme(scheme))
ctrlConfig := commonController.Config{ ctrlConfig := commonController.Config{
RestrictedNamespace: restrictedNamespace, RestrictedNamespace: restrictedNamespace,
VirtualServiceSupportsHTTPS: virtualServiceSupportsHTTPS,
} }
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
......
...@@ -24,7 +24,7 @@ const ( ...@@ -24,7 +24,7 @@ const (
// nolint: gosec // nolint: gosec
EnvApiStoreApiToken = "API_STORE_API_TOKEN" EnvApiStoreApiToken = "API_STORE_API_TOKEN"
EnvDynamoServicePort = "PORT" EnvDynamoServicePort = "DYNAMO_PORT"
EnvDockerRegistryServer = "DOCKER_REGISTRY_SERVER" EnvDockerRegistryServer = "DOCKER_REGISTRY_SERVER"
EnvDockerRegistrySecret = "DOCKER_REGISTRY_SECRET_NAME" EnvDockerRegistrySecret = "DOCKER_REGISTRY_SECRET_NAME"
......
...@@ -740,7 +740,7 @@ func (r *DynamoComponentDeploymentReconciler) generateIngress(ctx context.Contex ...@@ -740,7 +740,7 @@ func (r *DynamoComponentDeploymentReconciler) generateIngress(ctx context.Contex
Service: &networkingv1.IngressServiceBackend{ Service: &networkingv1.IngressServiceBackend{
Name: opt.dynamoComponentDeployment.Name, Name: opt.dynamoComponentDeployment.Name,
Port: networkingv1.ServiceBackendPort{ Port: networkingv1.ServiceBackendPort{
Number: 3000, Number: commonconsts.DynamoServicePort,
}, },
}, },
}, },
...@@ -800,7 +800,7 @@ func (r *DynamoComponentDeploymentReconciler) generateVirtualService(ctx context ...@@ -800,7 +800,7 @@ func (r *DynamoComponentDeploymentReconciler) generateVirtualService(ctx context
Destination: &istioNetworking.Destination{ Destination: &istioNetworking.Destination{
Host: opt.dynamoComponentDeployment.Name, Host: opt.dynamoComponentDeployment.Name,
Port: &istioNetworking.PortSelector{ Port: &istioNetworking.PortSelector{
Number: 3000, Number: commonconsts.DynamoServicePort,
}, },
}, },
}, },
...@@ -1121,7 +1121,7 @@ func (r *DynamoComponentDeploymentReconciler) generatePodTemplateSpec(ctx contex ...@@ -1121,7 +1121,7 @@ func (r *DynamoComponentDeploymentReconciler) generatePodTemplateSpec(ctx contex
// todo : remove this line when https://github.com/ai-dynamo/dynamo/issues/345 is fixed // todo : remove this line when https://github.com/ai-dynamo/dynamo/issues/345 is fixed
enableDependsOption := false enableDependsOption := false
if len(opt.dynamoComponentDeployment.Spec.ExternalServices) > 0 && enableDependsOption { if len(opt.dynamoComponentDeployment.Spec.ExternalServices) > 0 && enableDependsOption {
serviceSuffix := fmt.Sprintf("%s.svc.cluster.local:3000", opt.dynamoComponentDeployment.Namespace) serviceSuffix := fmt.Sprintf("%s.svc.cluster.local:%d", opt.dynamoComponentDeployment.Namespace, containerPort)
keys := make([]string, 0, len(opt.dynamoComponentDeployment.Spec.ExternalServices)) keys := make([]string, 0, len(opt.dynamoComponentDeployment.Spec.ExternalServices))
for key := range opt.dynamoComponentDeployment.Spec.ExternalServices { for key := range opt.dynamoComponentDeployment.Spec.ExternalServices {
......
...@@ -143,7 +143,7 @@ func (r *DynamoGraphDeploymentReconciler) Reconcile(ctx context.Context, req ctr ...@@ -143,7 +143,7 @@ func (r *DynamoGraphDeploymentReconciler) Reconcile(ctx context.Context, req ctr
} }
} }
if deployment.Spec.Ingress.Enabled { if deployment.Spec.Ingress.Enabled {
dynamoDeployment.SetEndpointStatus((r.isEndpointSecured()), getIngressHost(deployment.Spec.Ingress)) dynamoDeployment.SetEndpointStatus(r.isEndpointSecured(), getIngressHost(deployment.Spec.Ingress))
} }
} }
...@@ -238,6 +238,9 @@ func (r *DynamoGraphDeploymentReconciler) generateDefaultIngressSpec(dynamoDeplo ...@@ -238,6 +238,9 @@ func (r *DynamoGraphDeploymentReconciler) generateDefaultIngressSpec(dynamoDeplo
} }
func (r *DynamoGraphDeploymentReconciler) isEndpointSecured() bool { func (r *DynamoGraphDeploymentReconciler) isEndpointSecured() bool {
if r.VirtualServiceGateway != "" && r.Config.VirtualServiceSupportsHTTPS {
return true
}
return r.IngressControllerTLSSecret != "" return r.IngressControllerTLSSecret != ""
} }
......
...@@ -30,6 +30,8 @@ import ( ...@@ -30,6 +30,8 @@ import (
type Config struct { type Config struct {
// Enable resources filtering, only the resources belonging to the given namespace will be handled. // Enable resources filtering, only the resources belonging to the given namespace will be handled.
RestrictedNamespace string RestrictedNamespace string
// If true, assume VirtualService endpoints are HTTPS
VirtualServiceSupportsHTTPS bool
} }
func EphemeralDeploymentEventFilter(config Config) predicate.Predicate { func EphemeralDeploymentEventFilter(config Config) predicate.Predicate {
......
...@@ -66,10 +66,12 @@ type Autoscaling struct { ...@@ -66,10 +66,12 @@ type Autoscaling struct {
} }
type Config struct { type Config struct {
Dynamo *DynamoConfig `yaml:"dynamo,omitempty"` Dynamo *DynamoConfig `yaml:"dynamo,omitempty"`
Resources *Resources `yaml:"resources,omitempty"` Resources *Resources `yaml:"resources,omitempty"`
Traffic *Traffic `yaml:"traffic,omitempty"` Traffic *Traffic `yaml:"traffic,omitempty"`
Autoscaling *Autoscaling `yaml:"autoscaling,omitempty"` Autoscaling *Autoscaling `yaml:"autoscaling,omitempty"`
HttpExposed bool `yaml:"http_exposed,omitempty"`
ApiEndpoints []string `yaml:"api_endpoints,omitempty"`
} }
type ServiceConfig struct { type ServiceConfig struct {
...@@ -250,13 +252,13 @@ func GenerateDynamoComponentsDeployments(ctx context.Context, parentDynamoGraphD ...@@ -250,13 +252,13 @@ func GenerateDynamoComponentsDeployments(ctx context.Context, parentDynamoGraphD
deployment.Spec.DynamoNamespace = &dynamoNamespace deployment.Spec.DynamoNamespace = &dynamoNamespace
dynamoServices[service.Name] = fmt.Sprintf("%s/%s", service.Config.Dynamo.Name, dynamoNamespace) dynamoServices[service.Name] = fmt.Sprintf("%s/%s", service.Config.Dynamo.Name, dynamoNamespace)
labels[commonconsts.KubeLabelDynamoNamespace] = dynamoNamespace labels[commonconsts.KubeLabelDynamoNamespace] = dynamoNamespace
} else {
// dynamo is not enabled
if config.EntryService == service.Name {
// enable virtual service for the entry service
deployment.Spec.Ingress = *ingressSpec
}
} }
// Check http_exposed independently
if config.EntryService == service.Name && service.Config.HttpExposed {
deployment.Spec.Ingress = *ingressSpec
// TODO (maybe): add paths to IngressSpec
}
if service.Config.Resources != nil { if service.Config.Resources != nil {
deployment.Spec.Resources = &compounaiCommon.Resources{ deployment.Spec.Resources = &compounaiCommon.Resources{
Requests: &compounaiCommon.ResourceItem{ Requests: &compounaiCommon.ResourceItem{
......
...@@ -182,6 +182,7 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -182,6 +182,7 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
Name: "service1", Name: "service1",
Dependencies: []map[string]string{{"service": "service2"}}, Dependencies: []map[string]string{{"service": "service2"}},
Config: Config{ Config: Config{
HttpExposed: true,
Resources: &Resources{ Resources: &Resources{
CPU: "1", CPU: "1",
Memory: "1Gi", Memory: "1Gi",
...@@ -260,6 +261,10 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -260,6 +261,10 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
}, },
}, },
}, },
Status: v1alpha1.DynamoComponentDeploymentStatus{
Conditions: nil,
PodSelector: nil,
},
}, },
"service2": { "service2": {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
...@@ -283,6 +288,18 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -283,6 +288,18 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
commonconsts.KubeLabelDynamoComponent: "service2", commonconsts.KubeLabelDynamoComponent: "service2",
commonconsts.KubeLabelDynamoNamespace: "default", commonconsts.KubeLabelDynamoNamespace: "default",
}, },
Ingress: v1alpha1.IngressSpec{
Enabled: false,
Host: "",
UseVirtualService: false,
VirtualServiceGateway: nil,
HostPrefix: nil,
Annotations: nil,
Labels: nil,
TLS: nil,
HostSuffix: nil,
IngressControllerClassName: nil,
},
}, },
}, },
}, },
...@@ -309,6 +326,7 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -309,6 +326,7 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
Name: "service1", Name: "service1",
Dependencies: []map[string]string{{"service": "service2"}}, Dependencies: []map[string]string{{"service": "service2"}},
Config: Config{ Config: Config{
HttpExposed: true,
Resources: &Resources{ Resources: &Resources{
CPU: "1", CPU: "1",
Memory: "1Gi", Memory: "1Gi",
...@@ -374,12 +392,27 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -374,12 +392,27 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
DeploymentSelectorValue: "service2/dynamo-test-dynamographdeployment", DeploymentSelectorValue: "service2/dynamo-test-dynamographdeployment",
}, },
}, },
Ingress: v1alpha1.IngressSpec{}, Ingress: v1alpha1.IngressSpec{
Enabled: false,
Host: "",
UseVirtualService: false,
VirtualServiceGateway: nil,
HostPrefix: nil,
Annotations: nil,
Labels: nil,
TLS: nil,
HostSuffix: nil,
IngressControllerClassName: nil,
},
Labels: map[string]string{ Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1", commonconsts.KubeLabelDynamoComponent: "service1",
}, },
}, },
}, },
Status: v1alpha1.DynamoComponentDeploymentStatus{
Conditions: nil,
PodSelector: nil,
},
}, },
"service2": { "service2": {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
...@@ -403,6 +436,18 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -403,6 +436,18 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
commonconsts.KubeLabelDynamoComponent: "service2", commonconsts.KubeLabelDynamoComponent: "service2",
commonconsts.KubeLabelDynamoNamespace: "dynamo-test-dynamographdeployment", commonconsts.KubeLabelDynamoNamespace: "dynamo-test-dynamographdeployment",
}, },
Ingress: v1alpha1.IngressSpec{
Enabled: false,
Host: "",
UseVirtualService: false,
VirtualServiceGateway: nil,
HostPrefix: nil,
Annotations: nil,
Labels: nil,
TLS: nil,
HostSuffix: nil,
IngressControllerClassName: nil,
},
}, },
}, },
}, },
...@@ -462,6 +507,250 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) { ...@@ -462,6 +507,250 @@ func TestGenerateDynamoComponentsDeployments(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "Test GenerateDynamoComponentsDeployments ingress enabled by default",
args: args{
parentDynamoGraphDeployment: &v1alpha1.DynamoGraphDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment",
Namespace: "default",
},
Spec: v1alpha1.DynamoGraphDeploymentSpec{
DynamoGraph: "dynamocomponent:ac4e234",
},
},
config: &DynamoGraphConfig{
DynamoTag: "dynamocomponent:MyServiceIngressEnabled",
EntryService: "service1",
Services: []ServiceConfig{
{
Name: "service1",
Config: Config{
HttpExposed: true,
},
},
},
},
ingressSpec: &v1alpha1.IngressSpec{
Enabled: true,
Host: "test-dynamographdeployment",
},
},
want: map[string]*v1alpha1.DynamoComponentDeployment{
"service1": {
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment-service1",
Namespace: "default",
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
},
Spec: v1alpha1.DynamoComponentDeploymentSpec{
DynamoComponent: "dynamocomponent:ac4e234",
DynamoTag: "dynamocomponent:MyServiceIngressEnabled",
DynamoComponentDeploymentSharedSpec: v1alpha1.DynamoComponentDeploymentSharedSpec{
Annotations: nil,
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
ServiceName: "service1",
DynamoNamespace: nil,
Resources: nil,
Autoscaling: &v1alpha1.Autoscaling{
Enabled: false,
MinReplicas: 0,
MaxReplicas: 0,
Behavior: nil,
Metrics: nil,
},
Envs: nil,
EnvFromSecret: nil,
PVC: nil,
RunMode: nil,
ExternalServices: nil,
Ingress: v1alpha1.IngressSpec{
Enabled: true,
Host: "test-dynamographdeployment",
},
ExtraPodMetadata: nil,
ExtraPodSpec: nil,
LivenessProbe: nil,
ReadinessProbe: nil,
Replicas: nil,
},
},
Status: v1alpha1.DynamoComponentDeploymentStatus{
Conditions: nil,
PodSelector: nil,
},
},
},
wantErr: false,
},
{
name: "Test GenerateDynamoComponentsDeployments ingress explicitly disabled",
args: args{
parentDynamoGraphDeployment: &v1alpha1.DynamoGraphDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment",
Namespace: "default",
},
Spec: v1alpha1.DynamoGraphDeploymentSpec{
DynamoGraph: "dynamocomponent:ac4e234",
},
},
config: &DynamoGraphConfig{
DynamoTag: "dynamocomponent:MyServiceIngressDisabled",
Services: []ServiceConfig{
{
Name: "service1",
Config: Config{},
},
},
},
ingressSpec: &v1alpha1.IngressSpec{
Enabled: false,
},
},
want: map[string]*v1alpha1.DynamoComponentDeployment{
"service1": {
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment-service1",
Namespace: "default",
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
},
Spec: v1alpha1.DynamoComponentDeploymentSpec{
DynamoComponent: "dynamocomponent:ac4e234",
DynamoTag: "dynamocomponent:MyServiceIngressDisabled",
DynamoComponentDeploymentSharedSpec: v1alpha1.DynamoComponentDeploymentSharedSpec{
Annotations: nil,
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
ServiceName: "service1",
DynamoNamespace: nil,
Resources: nil,
Autoscaling: &v1alpha1.Autoscaling{
Enabled: false,
MinReplicas: 0,
MaxReplicas: 0,
Behavior: nil,
Metrics: nil,
},
Envs: nil,
EnvFromSecret: nil,
PVC: nil,
RunMode: nil,
ExternalServices: nil,
Ingress: v1alpha1.IngressSpec{
Enabled: false,
Host: "",
UseVirtualService: false,
VirtualServiceGateway: nil,
HostPrefix: nil,
Annotations: nil,
Labels: nil,
TLS: nil,
HostSuffix: nil,
IngressControllerClassName: nil,
},
ExtraPodMetadata: nil,
ExtraPodSpec: nil,
LivenessProbe: nil,
ReadinessProbe: nil,
Replicas: nil,
},
},
Status: v1alpha1.DynamoComponentDeploymentStatus{
Conditions: nil,
PodSelector: nil,
},
},
},
wantErr: false,
},
{
name: "Test GenerateDynamoComponentsDeployments ingress custom host",
args: args{
parentDynamoGraphDeployment: &v1alpha1.DynamoGraphDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment",
Namespace: "default",
},
Spec: v1alpha1.DynamoGraphDeploymentSpec{
DynamoGraph: "dynamocomponent:ac4e234",
},
},
config: &DynamoGraphConfig{
DynamoTag: "dynamocomponent:MyServiceIngressCustomHost",
EntryService: "service1",
Services: []ServiceConfig{
{
Name: "service1",
Config: Config{
HttpExposed: true,
},
},
},
},
ingressSpec: &v1alpha1.IngressSpec{
Enabled: true,
Host: "custom-host",
},
},
want: map[string]*v1alpha1.DynamoComponentDeployment{
"service1": {
ObjectMeta: metav1.ObjectMeta{
Name: "test-dynamographdeployment-service1",
Namespace: "default",
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
},
Spec: v1alpha1.DynamoComponentDeploymentSpec{
DynamoComponent: "dynamocomponent:ac4e234",
DynamoTag: "dynamocomponent:MyServiceIngressCustomHost",
DynamoComponentDeploymentSharedSpec: v1alpha1.DynamoComponentDeploymentSharedSpec{
Annotations: nil,
Labels: map[string]string{
commonconsts.KubeLabelDynamoComponent: "service1",
},
ServiceName: "service1",
DynamoNamespace: nil,
Resources: nil,
Autoscaling: &v1alpha1.Autoscaling{
Enabled: false,
MinReplicas: 0,
MaxReplicas: 0,
Behavior: nil,
Metrics: nil,
},
Envs: nil,
EnvFromSecret: nil,
PVC: nil,
RunMode: nil,
ExternalServices: nil,
Ingress: v1alpha1.IngressSpec{
Enabled: true,
Host: "custom-host",
},
ExtraPodMetadata: nil,
ExtraPodSpec: nil,
LivenessProbe: nil,
ReadinessProbe: nil,
Replicas: nil,
},
},
Status: v1alpha1.DynamoComponentDeploymentStatus{
Conditions: nil,
PodSelector: nil,
},
},
},
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
......
...@@ -86,7 +86,7 @@ spec: ...@@ -86,7 +86,7 @@ spec:
- name: WORKERS - name: WORKERS
value: "{{ .config.workers }}" value: "{{ .config.workers }}"
{{- end }} {{- end }}
- name: PORT - name: DYNAMO_PORT
value: "3000" value: "3000"
{{- if $.Values.natsAddr }} {{- if $.Values.natsAddr }}
- name: NATS_SERVER - name: NATS_SERVER
......
...@@ -37,6 +37,7 @@ from dynamo.runtime import DistributedRuntime, dynamo_endpoint, dynamo_worker ...@@ -37,6 +37,7 @@ from dynamo.runtime import DistributedRuntime, dynamo_endpoint, dynamo_worker
from dynamo.sdk import dynamo_context from dynamo.sdk import dynamo_context
from dynamo.sdk.cli.utils import append_dynamo_state from dynamo.sdk.cli.utils import append_dynamo_state
from dynamo.sdk.lib.service import LinkedServices from dynamo.sdk.lib.service import LinkedServices
from dynamo.sdk.lib.utils import get_host_port
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -313,16 +314,15 @@ def main( ...@@ -313,16 +314,15 @@ def main(
if added_routes: if added_routes:
# Configure uvicorn with graceful shutdown # Configure uvicorn with graceful shutdown
# get the port from PORT env var or use 8000 as default host, port = get_host_port()
port = int(os.environ.get("PORT", 8000))
config = uvicorn.Config( config = uvicorn.Config(
service.app, host="0.0.0.0", port=port, log_level="info" service.app, host=host, port=port, log_level="info"
) )
server = uvicorn.Server(config) server = uvicorn.Server(config)
# Start the server with graceful shutdown handling # Start the server with graceful shutdown handling
logger.info( logger.info(
f"Starting FastAPI server on 0.0.0.0:{port} with routes: {added_routes}" f"Starting FastAPI server on {config.host}:{config.port} with routes: {added_routes}"
) )
server.run() server.run()
else: else:
......
...@@ -133,10 +133,21 @@ class DynamoService(Service[T]): ...@@ -133,10 +133,21 @@ class DynamoService(Service[T]):
# Register Dynamo endpoints # Register Dynamo endpoints
self._dynamo_endpoints: Dict[str, DynamoEndpoint] = {} self._dynamo_endpoints: Dict[str, DynamoEndpoint] = {}
self._api_endpoints: list[str] = []
for field in dir(inner): for field in dir(inner):
value = getattr(inner, field) value = getattr(inner, field)
if isinstance(value, DynamoEndpoint): if isinstance(value, DynamoEndpoint):
self._dynamo_endpoints[value.name] = value self._dynamo_endpoints[value.name] = value
if getattr(value, "is_api", False):
# Ensure endpoint path starts with '/'
path = (
value.name if value.name.startswith("/") else f"/{value.name}"
)
self._api_endpoints.append(path)
# If any API endpoints exist, mark service as HTTP-exposed and list endpoints
if self._api_endpoints:
self.config["http_exposed"] = True
self.config["api_endpoints"] = self._api_endpoints.copy()
self._linked_services: List[DynamoService] = [] # Track linked services self._linked_services: List[DynamoService] = [] # Track linked services
......
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# 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.
import os
def get_host_port():
"""Gets host and port from environment variables. Defaults to 0.0.0.0:8000."""
port = int(os.environ.get("DYNAMO_PORT", 8000))
host = os.environ.get("DYNAMO_HOST", "0.0.0.0")
return host, port
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment