)]}'
{
  "commit": "13692b444b5c69b56a7bd3ba16105c3b8f8e94cd",
  "tree": "4b86f8e71eca6f3c41d29aefc3ed27396aed9e1d",
  "parents": [
    "151452f1e4c93bc2fcf26f71ae353ea0aff009ca"
  ],
  "author": {
    "name": "vishesh92",
    "email": "vishesh92@gmail.com",
    "time": "Mon Aug 31 13:53:02 2026 +0530"
  },
  "committer": {
    "name": "vishesh92",
    "email": "vishesh92@gmail.com",
    "time": "Wed Sep 02 12:38:30 2026 +0530"
  },
  "message": "Add a simulated dev/test environment and simulator-based e2e CI\n\nThe repository had no way to exercise the CCM end to end. The only\n\"run against real CloudStack\" hook was configFromEnv() in\ncloudstack_test.go, which skips unless CS_API_URL and friends are set,\nand nothing set them. As a result EnsureLoadBalancer,\nUpdateLoadBalancer and EnsureLoadBalancerDeleted -- the three functions\nholding nearly all of the load balancer branching -- had no test\ncoverage at all, and the README pointed at a Docker Hub image\n(cloudstack/simulator) that no longer exists.\n\nAdd hack/e2e, which brings up a CloudStack simulator, deploys its\nadvanced zone, mints admin API keys, creates a kind cluster and\ndeploys CloudStack VMs matching its nodes, then runs the CCM against\nboth. CloudStack calls go through cmk, so the scripts run the same\ncommands the documentation tells you to run, and cmk\u0027s own async job\nhandling removes any need to poll queryAsyncJobResult.\ndocs/development.md walks through the same steps by hand so the\nenvironment is understandable rather than magic.\n\nAdd a Go e2e suite under test/e2e covering load balancer lifecycle,\nnode initialization, service annotations and the VPC/network ACL path.\nIt is behind the e2e build tag, so it stays out of `make test` and\n`go build ./...`, and it needs no new module dependencies.\n\nRun all of it in CI as a matrix of the latest two Kubernetes minors\nagainst CloudStack 4.22.1.0 and 4.20.2.0. The CloudStack axis is not\nonly version coverage: 4.22 and later update a load balancer rule\u0027s\nCIDR list in place while earlier releases delete and recreate the\nrule, so both branches are exercised. Cells run in parallel and share\na single image build, and the simulator and kind node images are\ncached between runs, so the workflow costs about as much wall-clock\nas a single run.\n\nFix several latent bugs in the load balancer path that the new suite\nexposed. Three call sites fetched CloudStack resources without the\nconfigured project: updateNetworkACL (the network and its ACL list),\ngetNetworkIDFromIPAddress (the public IP and its network), and the\ndisassociation check in EnsureLoadBalancerDeleted. On a VPC owned by a\nproject this made every LoadBalancer service fail with \"error fetching\nNetwork with ID\" and never get an ingress address, and it leaked the\npublic IP on deletion. getNetworkIDFromIPAddress also reported a failed\nnetwork lookup as success by returning the wrong error variable, and\nguarded on Networkid while looking up Associatednetworkid; either could\nhand the caller an empty network ID, which GetNetworkByID does not\nreject but looks up as an unfiltered network list, so it could resolve\nto an arbitrary network instead of failing.\n\nFix a load balancer rule leak. CloudStack does not enforce unique rule\nnames, but loadBalancer.rules is keyed by name, so a duplicate silently\ndisplaced its twin in the map and then survived EnsureLoadBalancerDeleted\nwith no service left to reference it. Duplicates are now tracked\nseparately and removed on both reconcile and delete, without disturbing\nthe rule that is kept.\n\nHarden two paths that panicked on an unexpected management-server\nresponse: getManagementServerVersion sliced the version string to three\ncomponents without checking its length, crashing the controller at\nstartup on a short version, and getPublicIPAddress guarded on the result\ncount but indexed the slice, which would panic if the two disagreed.\n\nAlso add the local cloud-config, cmk-config and kube-config files to\n.gitignore. They hold live credentials and were previously untracked\nbut not ignored.\n\nFixes #4\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n",
  "tree_diff": [
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "e8582beec4abf73e2d47975cd4e86f9c1eed9da2",
      "new_mode": 33188,
      "new_path": ".github/workflows/e2e-simulator.yml"
    },
    {
      "type": "modify",
      "old_id": "79e12562b308983f7202a931425a40c98a29db94",
      "old_mode": 33188,
      "old_path": ".gitignore",
      "new_id": "73c4ecea648adfeb18159b6a8039758efc1cf1e5",
      "new_mode": 33188,
      "new_path": ".gitignore"
    },
    {
      "type": "modify",
      "old_id": "379c873f2681e8aea5fb7e3db1b86c3e9396f54a",
      "old_mode": 33188,
      "old_path": ".golangci.yml",
      "new_id": "84ae959e0935dceac770db93486a52591f1f042e",
      "new_mode": 33188,
      "new_path": ".golangci.yml"
    },
    {
      "type": "modify",
      "old_id": "bbb03f0dcb48573c14eed233b279e857cc295484",
      "old_mode": 33188,
      "old_path": "Makefile",
      "new_id": "6abed19ad6b4f4b83b6a50196094cc50e82e136e",
      "new_mode": 33188,
      "new_path": "Makefile"
    },
    {
      "type": "modify",
      "old_id": "fc4922b7ad605dcba8d0f1cc77d1d7538569297f",
      "old_mode": 33188,
      "old_path": "README.md",
      "new_id": "9d2d93a8e5dbdce8272e27a5ea8291c537cd5213",
      "new_mode": 33188,
      "new_path": "README.md"
    },
    {
      "type": "modify",
      "old_id": "2294de6f6541f9b1db6a06503315f1320a802431",
      "old_mode": 33188,
      "old_path": "cloudstack.go",
      "new_id": "d27d1d3afc7caa36aa0579546e24e482427b7567",
      "new_mode": 33188,
      "new_path": "cloudstack.go"
    },
    {
      "type": "modify",
      "old_id": "ffbdd7cdcfb79fb2d1b6131fc837f04b29fba79f",
      "old_mode": 33188,
      "old_path": "cloudstack_loadbalancer.go",
      "new_id": "4e6b6c2b2a80b9ac958d4b8980512b7b3554a47e",
      "new_mode": 33188,
      "new_path": "cloudstack_loadbalancer.go"
    },
    {
      "type": "modify",
      "old_id": "4bbf38e7705ad722d2b8bdc2dff6873694315d19",
      "old_mode": 33188,
      "old_path": "cloudstack_loadbalancer_test.go",
      "new_id": "02c040db705074ba226ef733a9dfc039037137b9",
      "new_mode": 33188,
      "new_path": "cloudstack_loadbalancer_test.go"
    },
    {
      "type": "modify",
      "old_id": "87ed02fd1deffed140a969fbd95272ab73806283",
      "old_mode": 33188,
      "old_path": "cloudstack_test.go",
      "new_id": "bcca41b16211d434b28e34d45382bd497583b2a9",
      "new_mode": 33188,
      "new_path": "cloudstack_test.go"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "73f3d7d53bfa85b9523492e71a41f6d57e1b8dba",
      "new_mode": 33188,
      "new_path": "docs/development.md"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "6e9c45572e843ac8c89e26bba21b794d6fbdb3b6",
      "new_mode": 33261,
      "new_path": "hack/e2e/10-simulator-up.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "0b1bace9a77f508cade519a4fe4e467ccc742cc6",
      "new_mode": 33261,
      "new_path": "hack/e2e/20-kind-up.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "3da04c69163c7d44f113bf2438ab9dd115d7ce29",
      "new_mode": 33261,
      "new_path": "hack/e2e/30-topology-isolated.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "71f39ab01d5f2ac6bf48fe85b222fe781d63ef3e",
      "new_mode": 33261,
      "new_path": "hack/e2e/40-ccm-deploy.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "4dae0e74f06d3b2fee2ef4d1835e67489a426677",
      "new_mode": 33261,
      "new_path": "hack/e2e/50-topology-vpc.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "c813367e09adeee8023fc542896d79f1ef8a035a",
      "new_mode": 33261,
      "new_path": "hack/e2e/90-collect-artifacts.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "682aa86f61b7b33c4414bd51c95345eee1c52aa2",
      "new_mode": 33261,
      "new_path": "hack/e2e/99-down.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "098acf4fa5dacb512a7b24322fcbb3f52824ab56",
      "new_mode": 33261,
      "new_path": "hack/e2e/env.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "5c79c241995403f72e09df39e868ffccfe376052",
      "new_mode": 33188,
      "new_path": "hack/e2e/kind-config.yaml"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "cc73e2a18a02c64f33afe03d8453af4212acaed4",
      "new_mode": 33188,
      "new_path": "hack/e2e/lib/cmk.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "ffe36d6c834d08167a0df0f7b2d200e3d1c33de5",
      "new_mode": 33188,
      "new_path": "hack/e2e/lib/log.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "9f050c90e7d926e7b19ac986c37cb7802f9bedce",
      "new_mode": 33261,
      "new_path": "hack/e2e/up.sh"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "0b12d441a51c201fbd5c398e9d6d78aefa0e4bfc",
      "new_mode": 33188,
      "new_path": "test/e2e/annotations_test.go"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "353c70f3f7de900a2ddfbe43352d162fcaafad8c",
      "new_mode": 33188,
      "new_path": "test/e2e/framework.go"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "c2eaae1a5fe038342ed7880214f195ef0d827fbd",
      "new_mode": 33188,
      "new_path": "test/e2e/loadbalancer_test.go"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "e94b00c62ae9fac8b19fc051638f1180dcf0fa99",
      "new_mode": 33188,
      "new_path": "test/e2e/node_test.go"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "89e48f6b2c712d14b52e41a07b1facc6d39af1ab",
      "new_mode": 33188,
      "new_path": "test/e2e/vpc_test.go"
    }
  ]
}
