func (ap *Platform) ValidateFunctionConfig(functionConfig *functionconfig.Config) error {
	if common.StringInSlice(functionConfig.Meta.Name, ap.ResolveReservedResourceNames()) {
		return nuclio.NewErrPreconditionFailed(fmt.Sprintf("Function name %s is reserved and cannot be used.",
			functionConfig.Meta.Name))
	}
	if err := ap.validateDockerImageFields(functionConfig); err != nil {
		return errors.Wrap(err, "Docker image fields validation failed")
	}
	if err := ap.validateTriggers(functionConfig); err != nil {
		return errors.Wrap(err, "Triggers validation failed")
	}
	if err := ap.validateMinMaxReplicas(functionConfig); err != nil {
		return errors.Wrap(err, "Min max replicas validation failed")
	}
	if err := ap.validateNodeSelector(functionConfig); err != nil {
		return errors.Wrap(err, "Node selector validation failed")
	}
	if err := ap.validateProjectExists(functionConfig); err != nil {
		return errors.Wrap(err, "Project existence validation failed")
	}
	if err := ap.validateVolumes(functionConfig); err != nil {
		return errors.Wrap(err, "Volumes validation failed")
	}
	if err := ap.validatePriorityClassName(functionConfig); err != nil {
		return errors.Wrap(err, "Priority class name validation failed")
	}
	return nil
}
ValidateFunctionConfig validaets and enforces of required function creation logic
package abstract
import (
	"bufio"
	"context"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"
	"github.com/nuclio/nuclio/pkg/common"
	"github.com/nuclio/nuclio/pkg/containerimagebuilderpusher"
	"github.com/nuclio/nuclio/pkg/errgroup"
	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/logprocessing"
	"github.com/nuclio/nuclio/pkg/opa"
	"github.com/nuclio/nuclio/pkg/platform"
	"github.com/nuclio/nuclio/pkg/platformconfig"
	"github.com/nuclio/nuclio/pkg/processor/build"
	"github.com/nuclio/nuclio/pkg/processor/build/runtime"
	"github.com/nuclio/nuclio/pkg/processor/trigger"
	"github.com/docker/distribution/reference"
	"github.com/google/go-cmp/cmp"
	"github.com/nuclio/errors"
	"github.com/nuclio/logger"
	"github.com/nuclio/nuclio-sdk-go"
	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/validation"
)
const (
	FunctionContainerHTTPPort            = 8080
	FunctionContainerWebAdminHTTPPort    = 8081
	FunctionContainerHealthCheckHTTPPort = 8082
	DefaultTargetCPU                     = 75
)
type Platform struct {
	Logger                  logger.Logger
	platform                platform.Platform
	invoker                 *invoker
	Config                  *platformconfig.Config
	ExternalIPAddresses     []string
	DeployLogStreams        *sync.Map
	ContainerBuilder        containerimagebuilderpusher.BuilderPusher
	ImageNamePrefixTemplate string
	DefaultNamespace        string
	OpaClient               opa.Client
}
func NewPlatform(parentLogger logger.Logger,
	platform platform.Platform,
	platformConfiguration *platformconfig.Config,
	defaultNamespace string) (*Platform, error) {
	var err error
	newPlatform := &Platform{
		Logger:           parentLogger.GetChild("platform"),
		platform:         platform,
		Config:           platformConfiguration,
		DeployLogStreams: &sync.Map{},
	}
	newPlatform.invoker, err = newInvoker(newPlatform.Logger, platform)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create invoker")
	}
	newPlatform.DefaultNamespace = defaultNamespace
	newPlatform.OpaClient = opa.CreateOpaClient(newPlatform.Logger, &platformConfiguration.Opa)
	return newPlatform, nil
}
func (ap *Platform) GetConfig() *platformconfig.Config {
	return ap.Config
}
func (ap *Platform) CreateFunctionBuild(createFunctionBuildOptions *platform.CreateFunctionBuildOptions) (
	*platform.CreateFunctionBuildResult, error) {
	builder, err := build.NewBuilder(createFunctionBuildOptions.Logger, ap.platform, &common.AbstractS3Client{})
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create builder")
	}
	return builder.Build(createFunctionBuildOptions)
}
func (ap *Platform) HandleDeployFunction(existingFunctionConfig *functionconfig.ConfigWithStatus,
	createFunctionOptions *platform.CreateFunctionOptions,
	onAfterConfigUpdated func() error,
	onAfterBuild func(*platform.CreateFunctionBuildResult, error) (*platform.CreateFunctionResult, error)) (
	*platform.CreateFunctionResult, error) {
	createFunctionOptions.Logger.InfoWith("Deploying function",
		"name", createFunctionOptions.FunctionConfig.Meta.Name)
	var buildResult *platform.CreateFunctionBuildResult
	var buildErr error
	onAfterConfigUpdatedWrapper := func(updatedFunctionConfig *functionconfig.Config) error {
		createFunctionOptions.FunctionConfig = *updatedFunctionConfig
		return onAfterConfigUpdated()
	}
	functionBuildRequired, err := ap.functionBuildRequired(&createFunctionOptions.FunctionConfig)
	if err != nil {
		return nil, errors.Wrap(err, "Failed determining whether function should build")
	}
	createFunctionOptions.FunctionConfig.Spec.Build.Mode = ""
	if functionBuildRequired && !functionconfig.ShouldSkipBuild(createFunctionOptions.FunctionConfig.Meta.Annotations) {
		buildResult, buildErr = ap.platform.CreateFunctionBuild(&platform.CreateFunctionBuildOptions{
			Logger:                     createFunctionOptions.Logger,
			FunctionConfig:             createFunctionOptions.FunctionConfig,
			PlatformName:               ap.platform.GetName(),
			OnAfterConfigUpdate:        onAfterConfigUpdatedWrapper,
			DependantImagesRegistryURL: createFunctionOptions.DependantImagesRegistryURL,
		})
		if buildErr == nil {
			createFunctionOptions.FunctionConfig.Spec.Image = buildResult.Image
			if createFunctionOptions.FunctionConfig.Spec.RunRegistry == "" {
				createFunctionOptions.FunctionConfig.Spec.RunRegistry =
					createFunctionOptions.FunctionConfig.Spec.Build.Registry
			}
			createFunctionOptions.FunctionConfig.Spec.Build.Timestamp = time.Now().Unix()
		}
	} else {
		createFunctionOptions.Logger.InfoWith("Skipping build",
			"name", createFunctionOptions.FunctionConfig.Meta.Name)
		if createFunctionOptions.FunctionConfig.Spec.Runtime == "" {
			return nil, errors.New("If image is passed, runtime must be specified")
		}
		if existingFunctionConfig != nil && createFunctionOptions.FunctionConfig.Spec.Image == "" {
			createFunctionOptions.FunctionConfig.Spec.Image = existingFunctionConfig.Spec.Image
		}
		if err = onAfterConfigUpdatedWrapper(&createFunctionOptions.FunctionConfig); err != nil {
			return nil, errors.Wrap(err, "Failed to trigger on after config update")
		}
	}
	deployResult, err := onAfterBuild(buildResult, buildErr)
	if buildErr != nil || err != nil {
		return nil, errors.Wrap(err, "Failed to deploy function")
	}
	if deployResult == nil {
		return nil, errors.New("Deployer returned no error, but nil deploy result")
	}
	if buildResult != nil {
		deployResult.CreateFunctionBuildResult = *buildResult
	}
	createFunctionOptions.Logger.InfoWith("Function deploy complete",
		"functionName", deployResult.UpdatedFunctionConfig.Meta.Name,
		"httpPort", deployResult.Port,
		"internalInvocationURLs", deployResult.FunctionStatus.InternalInvocationURLs,
		"externalInvocationURLs", deployResult.FunctionStatus.ExternalInvocationURLs)
	return deployResult, nil
}
func (ap *Platform) EnrichFunctionConfig(functionConfig *functionconfig.Config) error {
	if functionConfig.Meta.Labels == nil {
		functionConfig.Meta.Labels = map[string]string{}
	}
	ap.EnrichLabelsWithProjectName(functionConfig.Meta.Labels)
	if err := ap.enrichImageName(functionConfig); err != nil {
		return errors.Wrap(err, "Failed enriching image name")
	}
	ap.enrichMinMaxReplicas(functionConfig)
	if functionConfig.Spec.ImagePullSecrets == "" {
		functionConfig.Spec.ImagePullSecrets =
			ap.GetDefaultRegistryCredentialsSecretName()
	}
	if functionConfig.Spec.Runtime == "python" {
		functionConfig.Spec.Runtime = "python:3.6"
	}
	if err := ap.enrichTriggers(functionConfig); err != nil {
		return errors.Wrap(err, "Failed enriching triggers")
	}
	if functionConfig.Spec.SecurityContext == nil {
		functionConfig.Spec.SecurityContext = &v1.PodSecurityContext{}
	}
	if err := ap.enrichVolumes(functionConfig); err != nil {
		return errors.Wrap(err, "Failed enriching volumes")
	}
	return nil
}
func (ap *Platform) EnrichLabelsWithProjectName(labels map[string]string) {
	if labels[common.NuclioResourceLabelKeyProjectName] == "" {
		labels[common.NuclioResourceLabelKeyProjectName] = platform.DefaultProjectName
		ap.Logger.Debug("No project name specified. Setting to default")
	}
}
func (ap *Platform) enrichDefaultHTTPTrigger(functionConfig *functionconfig.Config) {
	if len(functionconfig.GetTriggersByKind(functionConfig.Spec.Triggers, "http")) > 0 {
		return
	}
	if functionConfig.Spec.Triggers == nil {
		functionConfig.Spec.Triggers = map[string]functionconfig.Trigger{}
	}
	defaultHTTPTrigger := functionconfig.GetDefaultHTTPTrigger()
	functionConfig.Spec.Triggers[defaultHTTPTrigger.Name] = defaultHTTPTrigger
}
func (ap *Platform) ValidateCreateFunctionOptionsAgainstExistingFunctionConfig(
	existingFunctionConfig *functionconfig.ConfigWithStatus,
	createFunctionOptions *platform.CreateFunctionOptions) error {
	if existingFunctionConfig == nil &&
		createFunctionOptions.FunctionConfig.Spec.Build.Mode == functionconfig.NeverBuild {
		return errors.New("Non existing function cannot be created with neverBuild mode")
	}
	if err := ap.ValidateResourceVersion(existingFunctionConfig, &createFunctionOptions.FunctionConfig); err != nil {
		return nuclio.WrapErrConflict(err)
	}
	if existingFunctionConfig != nil &&
		existingFunctionConfig.Status.State == functionconfig.FunctionStateImported &&
		createFunctionOptions.FunctionConfig.Spec.Disable {
		return errors.New("Failed to disable function: non-deployed functions cannot be disabled")
	}
	if existingFunctionConfig != nil &&
		len(existingFunctionConfig.Status.APIGateways) > 0 &&
		createFunctionOptions.FunctionConfig.Spec.Disable {
		ap.Logger.WarnWith("Disabling function with assigned api gateway validation failed",
			"functionName", existingFunctionConfig.Meta.Name,
			"apiGateways", existingFunctionConfig.Status.APIGateways)
		return nuclio.NewErrBadRequest("Cannot disable function while used by an API gateway")
	}
	return nil
}
func (ap *Platform) ValidateResourceVersion(functionConfigWithStatus *functionconfig.ConfigWithStatus,
	requestFunctionConfig *functionconfig.Config) error {
	if functionConfigWithStatus == nil {
		return nil
	}
	existingResourceVersion := functionConfigWithStatus.Meta.ResourceVersion
	requestResourceVersion := requestFunctionConfig.Meta.ResourceVersion
	if requestResourceVersion != "" &&
		requestResourceVersion != existingResourceVersion {
		ap.Logger.WarnWith("Function resource version is stale",
			"functionName", functionConfigWithStatus.Meta.Name,
			"requestResourceVersion", requestResourceVersion,
			"existingResourceVersion", existingResourceVersion)
		return errors.New("Function resource version is stale")
	}
	return nil
}
func (ap *Platform) EnrichFunctionsWithDeployLogStream(functions []platform.Function) {
	for _, function := range functions {
		if deployLogStream, exists := ap.DeployLogStreams.Load(function.GetConfig().Meta.GetUniqueID()); exists {
			deployLogStream.(*LogStream).ReadLogs(nil, &function.GetStatus().Logs)
		}
	}
}