// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//     http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 config

import (
	"fmt"
	"strings"

	cniSkel "github.com/containernetworking/cni/pkg/skel"
	cniTypes "github.com/containernetworking/cni/pkg/types"
)

// kubernetesArgs defines the Kubernetes arguments passed in CNI_ARGS environment variable.
type kubernetesArgs struct {
	cniTypes.CommonArgs
	K8S_POD_NAMESPACE          cniTypes.UnmarshallableString
	K8S_POD_NAME               cniTypes.UnmarshallableString
	K8S_POD_INFRA_CONTAINER_ID cniTypes.UnmarshallableString
}

// KubernetesConfig contains Kubernetes-specific configuration.
type KubernetesConfig struct {
	Namespace           string
	PodName             string
	PodInfraContainerID string
	ServiceCIDR         string
}

const (
	// namespacePlaceholder is the placeholder string to be replaced with the actual namespace.
	namespacePlaceholder = "{%namespace%}"

	// ignoreUnknown specifies whether unknown CNI arguments are ignored.
	ignoreUnknown = true
)

var (
	retrievePodConfigHandler func(netConfig *NetConfig) error
)

// parseKubernetesArgs parses Kubernetes-specific CNI arguments.
func parseKubernetesArgs(netConfig *NetConfig, args *cniSkel.CmdArgs, isAddCmd bool) error {
	if args == nil || args.Args == "" {
		return nil
	}

	// Parse the arguments in CNI_ARGS environment variable.
	var ka kubernetesArgs
	ka.IgnoreUnknown = ignoreUnknown

	err := cniTypes.LoadArgs(args.Args, &ka)
	if err != nil {
		return fmt.Errorf("failed to parse runtime args: %v", err)
	}

	kc := netConfig.Kubernetes
	kc.Namespace = string(ka.K8S_POD_NAMESPACE)
	kc.PodName = string(ka.K8S_POD_NAME)
	kc.PodInfraContainerID = string(ka.K8S_POD_INFRA_CONTAINER_ID)

	// The only additional information we need to query from API server is the pod IP address,
	// which is required only for ADD commands. Also the API server may have deleted the pod
	// object already in the DEL path.
	if !isAddCmd {
		return nil
	}

	if kc.Namespace == "" || kc.PodName == "" {
		return fmt.Errorf("missing required args %v", kc)
	}

	// Insert the actual namespace of the pod to the DNS suffix search list.
	for i, suffix := range netConfig.DNS.Search {
		netConfig.DNS.Search[i] = strings.Replace(suffix, namespacePlaceholder, kc.Namespace, 1)
	}

	// Retrieve any missing information not available in netconfig.
	if retrievePodConfigHandler != nil && len(netConfig.IPAddresses) == 0 {
		err = retrievePodConfigHandler(netConfig)
	}

	return err
}
