package main

import (
	"fmt"

	"github.com/lxc/lxd/shared"
)

func (suite *lxdTestSuite) TestContainer_ProfilesDefault() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		1,
		"No default profile created on containerCreateInternal.")

	suite.Equal(
		"default",
		profiles[0],
		"First profile should be the default profile.")
}

func (suite *lxdTestSuite) TestContainer_ProfilesMulti() {
	// Create an unprivileged profile
	_, err := dbProfileCreate(
		suite.d.db,
		"unprivileged",
		"unprivileged",
		map[string]string{"security.privileged": "true"},
		shared.Devices{})

	suite.Req.Nil(err, "Failed to create the unprivileged profile.")
	defer func() {
		dbProfileDelete(suite.d.db, "unprivileged")
	}()

	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Profiles:  []string{"default", "unprivileged"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		2,
		"Didn't get both profiles in containerCreateInternal.")

	suite.True(
		c.IsPrivileged(),
		"The container is not privileged (didn't apply the unprivileged profile?).")
}

func (suite *lxdTestSuite) TestContainer_ProfilesOverwriteDefaultNic() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: shared.Devices{
			"eth0": shared.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)

	suite.True(c.IsPrivileged(), "This container should be privileged.")

	out, _, err := c.Render()
	suite.Req.Nil(err)

	state := out.(*shared.ContainerInfo)
	defer c.Delete()

	suite.Equal(
		"unknownbr0",
		state.Devices["eth0"]["parent"],
		"Container config doesn't overwrite profile config.")
}

func (suite *lxdTestSuite) TestContainer_LoadFromDB() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: shared.Devices{
			"eth0": shared.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	// Create the container
	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	// Load the container and trigger initLXC()
	c2, err := containerLoadByName(suite.d, "testFoo")
	c2.IsRunning()
	suite.Req.Nil(err)

	suite.Exactly(
		c,
		c2,
		"The loaded container isn't excactly the same as the created one.")
}

func (suite *lxdTestSuite) TestContainer_Path_Regular() {
	// Regular
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.False(c.IsSnapshot(), "Shouldn't be a snapshot.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo"), c.Path())
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), containerPath("testFoo2", false))
}

func (suite *lxdTestSuite) TestContainer_Path_Snapshot() {
	// Snapshot
	args := containerArgs{
		Ctype:     cTypeSnapshot,
		Ephemeral: false,
		Name:      "test/snap0",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.True(c.IsSnapshot(), "Should be a snapshot.")
	suite.Req.Equal(
		shared.VarPath("snapshots", "test", "snap0"),
		c.Path())
	suite.Req.Equal(
		shared.VarPath("snapshots", "test", "snap1"),
		containerPath("test/snap1", true))
}

func (suite *lxdTestSuite) TestContainer_LogPath() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Equal(shared.VarPath("logs", "testFoo"), c.LogPath())
}

func (suite *lxdTestSuite) TestContainer_IsPrivileged_Privileged() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.True(c.IsPrivileged(), "This container should be privileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *lxdTestSuite) TestContainer_IsPrivileged_Unprivileged() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "false"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.False(c.IsPrivileged(), "This container should be unprivileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *lxdTestSuite) TestContainer_Rename() {
	args := containerArgs{
		Ctype:     cTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d, args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Nil(c.Rename("testFoo2"), "Failed to rename the container.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), c.Path())
}

func (suite *lxdTestSuite) TestContainer_findIdmap_isolated() {
	c1, err := containerCreateInternal(suite.d, containerArgs{
		Ctype: cTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d, containerArgs{
		Ctype: cTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmapSet()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmapSet()
	suite.Req.Nil(err)

	host := suite.d.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(0, map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(65536, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536*2, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(0, map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(65536, map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *lxdTestSuite) TestContainer_findIdmap_mixed() {
	c1, err := containerCreateInternal(suite.d, containerArgs{
		Ctype: cTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d, containerArgs{
		Ctype: cTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmapSet()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmapSet()
	suite.Req.Nil(err)

	host := suite.d.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(0, map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(host.Maprange, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(0, map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(65536, map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *lxdTestSuite) TestContainer_findIdmap_raw() {
	c1, err := containerCreateInternal(suite.d, containerArgs{
		Ctype: cTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
			"raw.idmap":               "both 1000 1000",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	map1, err := c1.(*containerLXC).NextIdmapSet()
	suite.Req.Nil(err)

	host := suite.d.IdmapSet.Idmap[0]

	for _, i := range []int{0, 3} {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(0, map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(1000, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{1, 4} {
		suite.Req.Equal(1000, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(1000, map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(1, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{2, 5} {
		suite.Req.Equal(host.Hostid+1001, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(1001, map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(host.Maprange-1000-1, map1.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *lxdTestSuite) TestContainer_findIdmap_maxed() {
	maps := []*shared.IdmapSet{}

	for i := 0; i < 7; i++ {
		c, err := containerCreateInternal(suite.d, containerArgs{
			Ctype: cTypeRegular,
			Name:  fmt.Sprintf("isol-%d", i),
			Config: map[string]string{
				"security.idmap.isolated": "true",
			},
		})

		/* we should fail if there are no ids left */
		if i != 6 {
			suite.Req.Nil(err)
		} else {
			suite.Req.NotNil(err)
			return
		}

		defer c.Delete()

		m, err := c.(*containerLXC).NextIdmapSet()
		suite.Req.Nil(err)

		maps = append(maps, m)
	}

	for i, m1 := range maps {
		for j, m2 := range maps {
			if m1 == m2 {
				continue
			}

			for _, e := range m2.Idmap {
				suite.Req.False(m1.HostidsIntersect(e), "%d and %d's idmaps intersect %v %v", i, j, m1, m2)
			}
		}
	}
}
