// Copyright 2017 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 engine

import (
	"net"

	"github.com/aws/amazon-ecs-cni-plugins/pkg/cniipamwrapper"
	"github.com/aws/amazon-ecs-cni-plugins/pkg/cniipwrapper"
	"github.com/aws/amazon-ecs-cni-plugins/pkg/netlinkwrapper"
	"github.com/containernetworking/cni/pkg/ns"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/pkg/errors"
	"github.com/vishvananda/netlink"
)

// configureVethContext wraps the parameters and the method to configure the
// veth interface in container's namespace
type configureVethContext struct {
	interfaceName string
	result        *current.Result
	ip            cniipwrapper.IP
	ipam          cniipamwrapper.IPAM
	netLink       netlinkwrapper.NetLink
}

func newConfigureVethContext(interfaceName string,
	result *current.Result,
	ip cniipwrapper.IP,
	ipam cniipamwrapper.IPAM,
	netLink netlinkwrapper.NetLink) *configureVethContext {

	return &configureVethContext{
		interfaceName: interfaceName,
		result:        result,
		ip:            ip,
		ipam:          ipam,
		netLink:       netLink,
	}
}

// run defines the closure to execute within the container's namespace to
// configure the veth interface
func (configContext *configureVethContext) run(hostNS ns.NetNS) error {
	// Add route for arp query request from host
	// same as: ip route add 169.254.172.1 via 169.254.172.1 dev ecs-eth0
	route := &types.Route{
		Dst: net.IPNet{
			IP:   configContext.result.IPs[0].Gateway,
			Mask: net.CIDRMask(32, 32),
		},
	}
	configContext.result.Routes = append(configContext.result.Routes, route)

	// Configure routes in the container
	err := configContext.ipam.ConfigureIface(
		configContext.interfaceName, configContext.result)
	if err != nil {
		return errors.Wrapf(err,
			"bridge configure veth: unable to configure interface: %s",
			configContext.interfaceName)
	}
	// Generate and set the hardware address for the interface, given
	// its IP
	err = configContext.ip.SetHWAddrByIP(
		configContext.interfaceName, configContext.result.IPs[0].Address.IP, nil)
	if err != nil {
		return errors.Wrapf(err,
			"bridge configure veth: unable to set hardware address for interface: %s",
			configContext.interfaceName)
	}

	link, err := configContext.netLink.LinkByName(configContext.interfaceName)
	if err != nil {
		return errors.Wrapf(err,
			"bridge configure veth: unable to get link for interface: %s",
			configContext.interfaceName)
	}

	routes, err := configContext.netLink.RouteList(link, netlink.FAMILY_ALL)
	if err != nil {
		return errors.Wrapf(err,
			"bridge configure veth: unable to fetch routes for interface: %s",
			configContext.interfaceName)
	}

	// Delete all default routes within the container
	for _, route := range routes {
		if route.Gw == nil {
			err = configContext.netLink.RouteDel(&route)
			if err != nil {
				return errors.Wrapf(err,
					"bridge configure veth: unable to delete route: %v", route)
			}
		}
	}

	return nil
}
