Răsfoiți Sursa

simplify logic using strict types

Feroze Mohideen 2 ani în urmă
părinte
comite
0e094a63d4
2 a modificat fișierele cu 229 adăugiri și 332 ștergeri
  1. 170 0
      internal/porter_app/v1/types.go
  2. 59 332
      internal/porter_app/v1/yaml.go

+ 170 - 0
internal/porter_app/v1/types.go

@@ -0,0 +1,170 @@
+package v1
+
+type v1_ServiceConfig struct {
+	// Autoscaling contains all configuration for autoscaling.  If enabled, ReplicaCount is ignored.
+	Autoscaling *Autoscaling `yaml:"autoscaling,omitempty" validate:"excluded_if=Type job"`
+	// Container contains configuration for Kubernetes container spec
+	Container Container `yaml:"container"`
+	// Health contains configuration for Kubernetes health probes
+	Health *Health `yaml:"health,omitempty" validate:"excluded_unless=Type web"`
+	// Ingress contains all configuration for ingress
+	Ingress Ingress `yaml:"ingress"`
+	// ReplicaCount is the number of replicas to run. Ignored if Autoscaling is enabled.
+	ReplicaCount string `yaml:"replicaCount"`
+	// Resources contains all configuration for resources requests
+	Resources Resources `yaml:"resources"`
+	// Service contains all configuration for the Kubernetes Service associated with the chart
+	Service Service `yaml:"service"`
+	// Labels contains all the labels to be included in controller specs
+	Labels map[string]string `yaml:"labels"`
+	// PodLabels contains all the labels to be included in pod specs
+	PodLabels map[string]string `yaml:"podLabels"`
+	// AllowConcurrent allows multiple instances of the job to run at the same time
+	AllowConcurrency bool `yaml:"allowConcurrent" validate:"excluded_unless=Type job"`
+	// Schedule is the cron schedule for the job
+	Schedule Schedule `yaml:"schedule" validate:"excluded_unless=Type job"`
+}
+
+// Schedule contains all configuration for job schedules
+type Schedule struct {
+	// Enabled specifies whether or not to use a schedule
+	Enabled bool `yaml:"enabled"`
+	// Value is the cron schedule
+	Value string `yaml:"value,omitempty"`
+}
+
+// Autoscaling contains all configuration for autoscaling in a web/worker chart.  If enabled, ReplicaCount is ignored.
+type Autoscaling struct {
+	// Enabled specifies whether or not to use autoscaling
+	Enabled bool `yaml:"enabled"`
+	// MaxReplicas is the maximum number of replicas to scale to
+	MaxReplicas string `yaml:"maxReplicas"`
+	// MinReplicas is the minimum number of replicas to scale to
+	MinReplicas string `yaml:"minReplicas"`
+	// TargetCPUUtilizationPercentage is the target CPU utilization percentage to scale on
+	TargetCPUUtilizationPercentage string `yaml:"targetCPUUtilizationPercentage"`
+	// TargetMemoryUtilizationPercentage is the target memory utilization percentage to scale on
+	TargetMemoryUtilizationPercentage string `yaml:"targetMemoryUtilizationPercentage"`
+}
+
+// Container contains all configuration for containers
+type Container struct {
+	// Command is the command to run in the container
+	Command string `yaml:"command"`
+	// Env contains the environment variables for the container
+	Env ContainerEnv `yaml:"env"`
+	// Port is the port that the container exposes
+	Port string `yaml:"port"`
+}
+
+// ContainerEnv represents the environment variables for a container
+type ContainerEnv struct {
+	// Normal represents the service-specific environment variables (as opposed to environment variables from synced env groups)
+	Normal map[string]string `yaml:"normal"`
+}
+
+// Health contains user-configurable health probes
+type Health struct {
+	// LivenessProbe checks whether a container should be considered healthy
+	LivenessProbe LivenessProbe `yaml:"livenessProbe"`
+	// ReadinessProbe checks whether a container should be considered ready to receive traffic
+	ReadinessProbe ReadinessProbe `yaml:"readinessProbe"`
+}
+
+// LivenessProbe contains user-configurable values for a liveness probe
+type LivenessProbe struct {
+	// Enabled specifies whether or not to use a liveness probe
+	Enabled bool `yaml:"enabled"`
+	// Path is the endpoint path to use for the probe
+	Path string `yaml:"path"`
+}
+
+// ReadinessProbe contains user-configurable values for a readiness probe
+type ReadinessProbe struct {
+	// Enabled specifies whether or not to use a readiness probe
+	Enabled bool `yaml:"enabled"`
+	// Path is the endpoint path to use for the probe
+	Path string `yaml:"path"`
+}
+
+// Image contains configuration for images
+type Image struct {
+	// Repository is url of the image repository where the image should be pulled from
+	Repository string `yaml:"repository"`
+	// Tag is the tag of the image to pull
+	Tag string `yaml:"tag"`
+}
+
+// Ingress contains configuration for ingress used by web charts
+type Ingress struct {
+	// Enabled specifies whether or not to use an ingress
+	Enabled bool `yaml:"enabled"`
+	// Hosts specifies the domains to include in the routing rules. If Ingress is enabled, hosts must not be empty.
+	Hosts []string `yaml:"hosts"`
+}
+
+// Resources is a wrapper over requests
+type Resources struct {
+	// Requests contains configuration for resource requests
+	Requests Requests `yaml:"requests"`
+}
+
+// Requests contains configuration for resource requests
+type Requests struct {
+	// Cpu is the cpu request (e.g. 100m - m for millicores)
+	Cpu string `yaml:"cpu"`
+	// Memory is the memory request (e.g. 100Mi - Mi for mebibytes)
+	Memory string `yaml:"memory"`
+}
+
+// Service contains configuration for exposing services
+type Service struct {
+	// Port is the port to expose the service on. This port should match the port in the container.
+	Port string `yaml:"port"`
+}
+
+type v1_PorterYAML struct {
+	Applications map[string]*Application `yaml:"applications" validate:"required_without=Services Apps"`
+	Version      *string                 `yaml:"version"`
+	Build        *Build                  `yaml:"build"`
+	Env          map[string]string       `yaml:"env"`
+	SyncedEnv    []*SyncedEnvSection     `yaml:"synced_env"`
+	Apps         map[string]v1_Service   `yaml:"apps" validate:"required_without=Applications Services"`
+	Services     map[string]v1_Service   `yaml:"services" validate:"required_without=Applications Apps"`
+
+	Release *v1_Service `yaml:"release"`
+}
+
+type Application struct {
+	Services map[string]v1_Service `yaml:"services" validate:"required"`
+	Build    *Build                `yaml:"build"`
+	Env      map[string]string     `yaml:"env"`
+
+	Release *v1_Service `yaml:"release"`
+}
+
+type Build struct {
+	Context    string   `yaml:"context" validate:"dir"`
+	Method     string   `yaml:"method" validate:"required,oneof=pack docker registry"`
+	Builder    string   `yaml:"builder" validate:"required_if=Method pack"`
+	Buildpacks []string `yaml:"buildpacks"`
+	Dockerfile string   `yaml:"dockerfile" validate:"required_if=Method docker"`
+	Image      string   `yaml:"image" validate:"required_if=Method registry"`
+}
+
+type v1_Service struct {
+	Run    string           `yaml:"run"`
+	Config v1_ServiceConfig `yaml:"config"`
+	Type   string           `yaml:"type" validate:"required, oneof=web worker job"`
+}
+
+type SyncedEnvSection struct {
+	Name    string                `json:"name" yaml:"name"`
+	Version uint                  `json:"version" yaml:"version"`
+	Keys    []SyncedEnvSectionKey `json:"keys" yaml:"keys"`
+}
+
+type SyncedEnvSectionKey struct {
+	Name   string `json:"name" yaml:"name"`
+	Secret bool   `json:"secret" yaml:"secret"`
+}

+ 59 - 332
internal/porter_app/v1/yaml.go

@@ -22,7 +22,7 @@ func AppProtoFromYaml(ctx context.Context, porterYamlBytes []byte) (*porterv1.Po
 		return nil, telemetry.Error(ctx, span, nil, "porter yaml is nil")
 	}
 
-	porterYaml := &PorterStackYAML{}
+	porterYaml := &v1_PorterYAML{}
 	err := yaml.Unmarshal(porterYamlBytes, porterYaml)
 	if err != nil {
 		return nil, telemetry.Error(ctx, span, err, "error unmarshaling porter yaml")
@@ -59,7 +59,7 @@ func AppProtoFromYaml(ctx context.Context, porterYamlBytes []byte) (*porterv1.Po
 	if porterYaml.Apps != nil && porterYaml.Services != nil {
 		return nil, telemetry.Error(ctx, span, nil, "'apps' and 'services' are synonymous but both were defined")
 	}
-	var services map[string]Service
+	var services map[string]v1_Service
 	if porterYaml.Apps != nil {
 		services = porterYaml.Apps
 	}
@@ -99,53 +99,7 @@ func AppProtoFromYaml(ctx context.Context, porterYamlBytes []byte) (*porterv1.Po
 	return appProto, nil
 }
 
-type PorterStackYAML struct {
-	Applications map[string]*Application `yaml:"applications" validate:"required_without=Services Apps"`
-	Version      *string                 `yaml:"version"`
-	Build        *Build                  `yaml:"build"`
-	Env          map[string]string       `yaml:"env"`
-	SyncedEnv    []*SyncedEnvSection     `yaml:"synced_env"`
-	Apps         map[string]Service      `yaml:"apps" validate:"required_without=Applications Services"`
-	Services     map[string]Service      `yaml:"services" validate:"required_without=Applications Apps"`
-
-	Release *Service `yaml:"release"`
-}
-
-type Application struct {
-	Services map[string]Service `yaml:"services" validate:"required"`
-	Build    *Build             `yaml:"build"`
-	Env      map[string]string  `yaml:"env"`
-
-	Release *Service `yaml:"release"`
-}
-
-type Build struct {
-	Context    string   `yaml:"context" validate:"dir"`
-	Method     string   `yaml:"method" validate:"required,oneof=pack docker registry"`
-	Builder    string   `yaml:"builder" validate:"required_if=Method pack"`
-	Buildpacks []string `yaml:"buildpacks"`
-	Dockerfile string   `yaml:"dockerfile" validate:"required_if=Method docker"`
-	Image      string   `yaml:"image" validate:"required_if=Method registry"`
-}
-
-type Service struct {
-	Run    string                 `yaml:"run"`
-	Config map[string]interface{} `yaml:"config"`
-	Type   string                 `yaml:"type" validate:"required, oneof=web worker job"`
-}
-
-type SyncedEnvSection struct {
-	Name    string                `json:"name" yaml:"name"`
-	Version uint                  `json:"version" yaml:"version"`
-	Keys    []SyncedEnvSectionKey `json:"keys" yaml:"keys"`
-}
-
-type SyncedEnvSectionKey struct {
-	Name   string `json:"name" yaml:"name"`
-	Secret bool   `json:"secret" yaml:"secret"`
-}
-
-func protoEnumFromType(name string, service Service) (porterv1.ServiceType, error) {
+func protoEnumFromType(name string, service v1_Service) (porterv1.ServiceType, error) {
 	var serviceType porterv1.ServiceType
 
 	if service.Type != "" {
@@ -181,31 +135,18 @@ func protoEnumFromType(name string, service Service) (porterv1.ServiceType, erro
 	return serviceType, errors.New("no type provided and could not parse service type from name")
 }
 
-func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (*porterv1.Service, error) {
-	if service.Config != nil {
-		service.Config = convertMap(service.Config).(map[string]interface{})
-	}
-
-	var instances int
-	if service.Config != nil && service.Config["replicaCount"] != nil {
-		parsedInstancesInt, err := convertToInt(service.Config["replicaCount"])
-		if err != nil {
-			return nil, fmt.Errorf("error converting instances: %w", err)
-		}
-		instances = parsedInstancesInt
+func serviceProtoFromConfig(service v1_Service, serviceType porterv1.ServiceType) (*porterv1.Service, error) {
+	serviceProto := &porterv1.Service{
+		Run:  service.Run,
+		Type: serviceType,
 	}
 
-	var cpuCores float32
-	var ramMegabytes int
-
-	requestsMap, err := getNestedMap(service.Config, "resources", "requests")
-	if err == nil && requestsMap != nil {
-		parsedCpuCores := requestsMap["cpu"]
-		cpuCoresStr, ok := parsedCpuCores.(string)
-		if !ok {
-			return nil, fmt.Errorf("cpu is not a string")
-		}
+	// if the revision number cannot be converted, it will default to 0
+	replicaCount, _ := strconv.Atoi(service.Config.ReplicaCount)
+	serviceProto.Instances = int32(replicaCount)
 
+	if service.Config.Resources.Requests.Cpu != "" {
+		cpuCoresStr := service.Config.Resources.Requests.Cpu
 		if !strings.HasSuffix(cpuCoresStr, "m") {
 			return nil, fmt.Errorf("cpu is not in millicores")
 		}
@@ -215,94 +156,29 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 		if err != nil {
 			return nil, fmt.Errorf("cpu is not a float")
 		}
-		cpuCores = float32(cpuCoresFloat64) / 1000
-
-		parsedRamMegabytes := requestsMap["memory"]
-		ramMegabytesStr, ok := parsedRamMegabytes.(string)
-		if !ok {
-			return nil, fmt.Errorf("memory is not a string")
-		}
-
-		if !strings.HasSuffix(ramMegabytesStr, "Mi") {
-			return nil, fmt.Errorf("memory is not in Mi")
-		}
-
-		ramMegabytesStr = strings.TrimSuffix(ramMegabytesStr, "Mi")
-		ramMegabytesInt, err := strconv.Atoi(ramMegabytesStr)
-		if err != nil {
-			return nil, fmt.Errorf("memory is not an int")
-		}
-		ramMegabytes = ramMegabytesInt
+		serviceProto.CpuCores = float32(cpuCoresFloat64) / 1000
 	}
 
-	var port int
-	containerMap, err := getNestedMap(service.Config, "container")
-	if err == nil && containerMap != nil {
-		parsedPort := containerMap["port"]
-		portStr, ok := parsedPort.(string)
-		if !ok {
-			return nil, fmt.Errorf("port is not a string")
+	if service.Config.Resources.Requests.Memory != "" {
+		memoryStr := service.Config.Resources.Requests.Memory
+		if !strings.HasSuffix(memoryStr, "Mi") {
+			return nil, fmt.Errorf("memory is not in Mi")
 		}
 
-		portInt, err := strconv.Atoi(portStr)
+		memoryStr = strings.TrimSuffix(memoryStr, "Mi")
+		memoryFloat64, err := strconv.ParseFloat(memoryStr, 32)
 		if err != nil {
-			return nil, fmt.Errorf("port is not an int")
+			return nil, fmt.Errorf("memory is not a float")
 		}
-
-		port = portInt
+		serviceProto.RamMegabytes = int32(memoryFloat64)
 	}
 
-	autoscalingMap, err := getNestedMap(service.Config, "autoscaling")
-	autoscalingExists := err == nil && autoscalingMap != nil
-	var autoscalingEnabled bool
-	var autoscalingMinInstances int
-	var autoscalingMaxInstances int
-	var autoscalingCpuThresholdPercent int
-	var autoscalingMemoryThresholdPercent int
-	if autoscalingExists {
-		parsedEnabled := autoscalingMap["enabled"]
-		parsedEnabledBool, err := convertToBool(parsedEnabled)
+	if service.Config.Container.Port != "" {
+		port, err := strconv.Atoi(service.Config.Container.Port)
 		if err != nil {
-			return nil, fmt.Errorf("error converting autoscaling enabled: %w", err)
+			return nil, fmt.Errorf("invalid port '%s'", service.Config.Container.Port)
 		}
-		autoscalingEnabled = parsedEnabledBool
-
-		parsedMinInstances := autoscalingMap["minReplicas"]
-		parsedMinInstancesInt, err := convertToInt(parsedMinInstances)
-		if err != nil {
-			return nil, fmt.Errorf("error converting autoscaling min instances: %w", err)
-		}
-		autoscalingMinInstances = parsedMinInstancesInt
-
-		parsedMaxInstances := autoscalingMap["maxReplicas"]
-		parsedMaxInstancesInt, err := convertToInt(parsedMaxInstances)
-		if err != nil {
-			return nil, fmt.Errorf("error converting autoscaling max instances: %w", err)
-		}
-		autoscalingMaxInstances = parsedMaxInstancesInt
-
-		parsedCpuThresholdPercent := autoscalingMap["targetCPUUtilizationPercentage"]
-		parsedCpuThresholdPercentInt, err := convertToInt(parsedCpuThresholdPercent)
-		if err != nil {
-			return nil, fmt.Errorf("error converting autoscaling cpu threshold percent: %w", err)
-		}
-		autoscalingCpuThresholdPercent = parsedCpuThresholdPercentInt
-
-		parsedMemoryThresholdPercent := autoscalingMap["targetMemoryUtilizationPercentage"]
-		parsedMemoryThresholdPercentInt, err := convertToInt(parsedMemoryThresholdPercent)
-		if err != nil {
-			return nil, fmt.Errorf("error converting autoscaling memory threshold percent: %w", err)
-		}
-		autoscalingMemoryThresholdPercent = parsedMemoryThresholdPercentInt
-	}
-
-	serviceProto := &porterv1.Service{
-		Run:          service.Run,
-		Type:         serviceType,
-		Instances:    int32(instances),
-		CpuCores:     cpuCores,
-		RamMegabytes: int32(ramMegabytes),
-		Port:         int32(port),
+		serviceProto.Port = int32(port)
 	}
 
 	switch serviceType {
@@ -314,81 +190,40 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 		webConfig := &porterv1.WebServiceConfig{}
 
 		var autoscaling *porterv1.Autoscaling
-		if autoscalingExists && autoscalingEnabled {
+		if service.Config.Autoscaling != nil && service.Config.Autoscaling.Enabled {
 			autoscaling = &porterv1.Autoscaling{
-				Enabled:                autoscalingEnabled,
-				MinInstances:           int32(autoscalingMinInstances),
-				MaxInstances:           int32(autoscalingMaxInstances),
-				CpuThresholdPercent:    int32(autoscalingCpuThresholdPercent),
-				MemoryThresholdPercent: int32(autoscalingMemoryThresholdPercent),
+				Enabled: service.Config.Autoscaling.Enabled,
 			}
+			minReplicas, _ := strconv.Atoi(service.Config.Autoscaling.MinReplicas)
+			autoscaling.MinInstances = int32(minReplicas)
+			maxReplicas, _ := strconv.Atoi(service.Config.Autoscaling.MaxReplicas)
+			autoscaling.MaxInstances = int32(maxReplicas)
+			cpuThresholdPercent, _ := strconv.Atoi(service.Config.Autoscaling.TargetCPUUtilizationPercentage)
+			autoscaling.CpuThresholdPercent = int32(cpuThresholdPercent)
+			memoryThresholdPercent, _ := strconv.Atoi(service.Config.Autoscaling.TargetMemoryUtilizationPercentage)
+			autoscaling.MemoryThresholdPercent = int32(memoryThresholdPercent)
 		}
 		webConfig.Autoscaling = autoscaling
 
-		var healthCheckEnabled bool
-		var healthCheckHttpPath string
-
-		// note that we are only reading from the readiness probe config, since readiness and liveness share the same config now
-		readinessProbeMap, err := getNestedMap(service.Config, "health", "readinessProbe")
-		healthCheckExists := err == nil && readinessProbeMap != nil
-		if healthCheckExists {
-			parsedHealthCheckEnabled := readinessProbeMap["enabled"]
-			parsedHealthCheckEnabledBool, err := convertToBool(parsedHealthCheckEnabled)
-			if err != nil {
-				return nil, fmt.Errorf("error converting health check enabled: %w", err)
-			}
-			healthCheckEnabled = parsedHealthCheckEnabledBool
-
-			parsedHealthCheckHttpPath := readinessProbeMap["path"]
-			parsedHealthCheckHttpPathStr, err := convertToString(parsedHealthCheckHttpPath)
-			if err != nil {
-				return nil, fmt.Errorf("error converting health check http path: %w", err)
-			}
-			healthCheckHttpPath = parsedHealthCheckHttpPathStr
-		}
-
 		var healthCheck *porterv1.HealthCheck
-		if healthCheckExists {
+		// note that we are only reading from the readiness probe config, since readiness and liveness share the same config now
+		if service.Config.Health != nil {
 			healthCheck = &porterv1.HealthCheck{
-				Enabled:  healthCheckEnabled,
-				HttpPath: healthCheckHttpPath,
+				Enabled:  service.Config.Health.ReadinessProbe.Enabled,
+				HttpPath: service.Config.Health.ReadinessProbe.Path,
 			}
 		}
 		webConfig.HealthCheck = healthCheck
 
-		ingressMap, err := getNestedMap(service.Config, "ingress")
-		ingressExists := err == nil && ingressMap != nil
-		var ingressEnabled bool
-		if ingressExists {
-			parsedIngressEnabled := ingressMap["enabled"]
-			parsedIngressEnabledBool, err := convertToBool(parsedIngressEnabled)
-			if err != nil {
-				return nil, fmt.Errorf("error converting ingress enabled: %w", err)
-			}
-			ingressEnabled = parsedIngressEnabledBool
-		}
-		webConfig.Private = !ingressEnabled
-
-		if ingressExists && ingressEnabled {
-			domains := make([]*porterv1.Domain, 0)
-			customDomains := ingressMap["hosts"]
-			if customDomains != nil {
-				customDomainsArr, ok := customDomains.([]interface{})
-				if !ok {
-					return nil, fmt.Errorf("error converting custom domains to array")
-				}
-				for _, domain := range customDomainsArr {
-					domainStr, ok := domain.(string)
-					if !ok {
-						return nil, fmt.Errorf("error converting custom domain to string")
-					}
-					domains = append(domains, &porterv1.Domain{
-						Name: domainStr,
-					})
-				}
-			}
-			webConfig.Domains = domains
+		domains := make([]*porterv1.Domain, 0)
+		for _, domain := range service.Config.Ingress.Hosts {
+			hostName := domain
+			domains = append(domains, &porterv1.Domain{
+				Name: hostName,
+			})
 		}
+		webConfig.Domains = domains
+		webConfig.Private = !service.Config.Ingress.Enabled
 
 		serviceProto.Config = &porterv1.Service_WebConfig{
 			WebConfig: webConfig,
@@ -397,14 +232,18 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 		workerConfig := &porterv1.WorkerServiceConfig{}
 
 		var autoscaling *porterv1.Autoscaling
-		if autoscalingExists && autoscalingEnabled {
+		if service.Config.Autoscaling != nil && service.Config.Autoscaling.Enabled {
 			autoscaling = &porterv1.Autoscaling{
-				Enabled:                autoscalingEnabled,
-				MinInstances:           int32(autoscalingMinInstances),
-				MaxInstances:           int32(autoscalingMaxInstances),
-				CpuThresholdPercent:    int32(autoscalingCpuThresholdPercent),
-				MemoryThresholdPercent: int32(autoscalingMemoryThresholdPercent),
+				Enabled: service.Config.Autoscaling.Enabled,
 			}
+			minReplicas, _ := strconv.Atoi(service.Config.Autoscaling.MinReplicas)
+			autoscaling.MinInstances = int32(minReplicas)
+			maxReplicas, _ := strconv.Atoi(service.Config.Autoscaling.MaxReplicas)
+			autoscaling.MaxInstances = int32(maxReplicas)
+			cpuThresholdPercent, _ := strconv.Atoi(service.Config.Autoscaling.TargetCPUUtilizationPercentage)
+			autoscaling.CpuThresholdPercent = int32(cpuThresholdPercent)
+			memoryThresholdPercent, _ := strconv.Atoi(service.Config.Autoscaling.TargetMemoryUtilizationPercentage)
+			autoscaling.MemoryThresholdPercent = int32(memoryThresholdPercent)
 		}
 		workerConfig.Autoscaling = autoscaling
 
@@ -412,29 +251,9 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 			WorkerConfig: workerConfig,
 		}
 	case porterv1.ServiceType_SERVICE_TYPE_JOB:
-		var allowConcurrent bool
-		if service.Config != nil && service.Config["allowConcurrent"] != nil {
-			parsedAllowConcurrentBool, err := convertToBool(service.Config["allowConcurrent"])
-			if err != nil {
-				return nil, fmt.Errorf("error converting allow concurrency: %w", err)
-			}
-			allowConcurrent = parsedAllowConcurrentBool
-		}
-
-		var cron string
-		cronScheduleMap, err := getNestedMap(service.Config, "schedule")
-		if err == nil && cronScheduleMap != nil {
-			parsedCron := cronScheduleMap["value"]
-			parsedConString, err := convertToString(parsedCron)
-			if err != nil {
-				return nil, fmt.Errorf("error converting cron schedule: %w", err)
-			}
-			cron = parsedConString
-		}
-
 		jobConfig := &porterv1.JobServiceConfig{
-			AllowConcurrent: allowConcurrent,
-			Cron:            cron,
+			AllowConcurrent: service.Config.AllowConcurrency,
+			Cron:            service.Config.Schedule.Value,
 		}
 
 		serviceProto.Config = &porterv1.Service_JobConfig{
@@ -444,95 +263,3 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 
 	return serviceProto, nil
 }
-
-func getNestedMap(obj map[string]interface{}, fields ...string) (map[string]interface{}, error) {
-	var res map[string]interface{}
-	curr := obj
-
-	for _, field := range fields {
-		objField, ok := curr[field]
-
-		if !ok {
-			return nil, fmt.Errorf("%s does not exist in object", field)
-		}
-
-		res, ok = objField.(map[string]interface{})
-
-		if !ok {
-			return nil, fmt.Errorf("%s is not a nested object", field)
-		}
-
-		curr = res
-	}
-
-	return res, nil
-}
-
-func convertToInt(input interface{}) (int, error) {
-	if input == nil {
-		return 0, nil
-	}
-
-	switch value := input.(type) {
-	case int:
-		return value, nil
-	case string:
-		return strconv.Atoi(value)
-	default:
-		return 0, fmt.Errorf("input is not an int or string")
-	}
-}
-
-func convertToBool(input interface{}) (bool, error) {
-	if input == nil {
-		return false, nil
-	}
-
-	switch value := input.(type) {
-	case bool:
-		return value, nil
-	case string:
-		return strconv.ParseBool(value)
-	default:
-		return false, fmt.Errorf("input is not a bool or string")
-	}
-}
-
-func convertToString(input interface{}) (string, error) {
-	if input == nil {
-		return "", nil
-	}
-
-	switch value := input.(type) {
-	case string:
-		return value, nil
-	default:
-		return "", fmt.Errorf("input is not a string")
-	}
-}
-
-func convertMap(m interface{}) interface{} {
-	switch m := m.(type) {
-	case map[string]interface{}:
-		for k, v := range m {
-			m[k] = convertMap(v)
-		}
-	case map[string]string:
-		result := map[string]interface{}{}
-		for k, v := range m {
-			result[k] = v
-		}
-		return result
-	case map[interface{}]interface{}:
-		result := map[string]interface{}{}
-		for k, v := range m {
-			result[k.(string)] = convertMap(v)
-		}
-		return result
-	case []interface{}:
-		for i, v := range m {
-			m[i] = convertMap(v)
-		}
-	}
-	return m
-}