From 545528f8cafc8a4a58e93dd9ef7f355094471f43 Mon Sep 17 00:00:00 2001 From: Ye Chen <127243817+yec-akamai@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:20:13 -0400 Subject: [PATCH 01/24] Support LKE-E: Bring Your Own VPC, Dual Stack Cluster, Audit Log Enabling (#803) * lkee * add stack type const * update monthly transfer fixture --- lke_clusters.go | 17 + lke_clusters_control_plane.go | 6 + .../TestInstance_GetMonthlyTransfer.yaml | 565 ++++++++++-------- ...estLKECluster_Enterprise_BYOVPC_smoke.yaml | 256 ++++++++ .../TestLKECluster_Enterprise_VPC_smoke.yaml | 563 +++++++++++++++++ test/integration/lke_clusters_test.go | 45 ++ test/unit/fixtures/lke_cluster_create.json | 8 +- test/unit/fixtures/lke_cluster_get.json | 8 +- test/unit/fixtures/lke_cluster_list.json | 8 +- test/unit/fixtures/lke_cluster_update.json | 5 +- test/unit/lke_clusters_test.go | 22 + 11 files changed, 1254 insertions(+), 249 deletions(-) create mode 100644 test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml create mode 100644 test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml diff --git a/lke_clusters.go b/lke_clusters.go index 2b6b1d4bc..f5a56fccd 100644 --- a/lke_clusters.go +++ b/lke_clusters.go @@ -18,6 +18,13 @@ const ( LKEClusterNotReady LKEClusterStatus = "not_ready" ) +type LKEClusterStackType string + +const ( + LKEClusterStackIPv4 LKEClusterStackType = "ipv4" + LKEClusterDualStack LKEClusterStackType = "ipv4-ipv6" +) + // LKECluster represents a LKECluster object type LKECluster struct { ID int `json:"id"` @@ -35,6 +42,11 @@ type LKECluster struct { // NOTE: APLEnabled is currently in beta and may only function with API version v4beta. APLEnabled bool `json:"apl_enabled"` + + // NOTE: SubnetID, VpcID, and StackType may not currently be available to all users and can only be used with v4beta. + SubnetID int `json:"subnet_id"` + VpcID int `json:"vpc_id"` + StackType LKEClusterStackType `json:"stack_type"` } // LKEClusterCreateOptions fields are those accepted by CreateLKECluster @@ -51,6 +63,11 @@ type LKEClusterCreateOptions struct { // NOTE: APLEnabled is currently in beta and may only function with API version v4beta. APLEnabled bool `json:"apl_enabled,omitempty"` + + // NOTE: SubnetID, VpcID, and StackType may not currently be available to all users and can only be used with v4beta. + SubnetID *int `json:"subnet_id,omitempty"` + VpcID *int `json:"vpc_id,omitempty"` + StackType *LKEClusterStackType `json:"stack_type,omitempty"` } // LKEClusterUpdateOptions fields are those accepted by UpdateLKECluster diff --git a/lke_clusters_control_plane.go b/lke_clusters_control_plane.go index 75b4d28c7..5a3544a25 100644 --- a/lke_clusters_control_plane.go +++ b/lke_clusters_control_plane.go @@ -5,6 +5,9 @@ import "context" // LKEClusterControlPlane fields contained within the `control_plane` attribute of an LKE cluster. type LKEClusterControlPlane struct { HighAvailability bool `json:"high_availability"` + + // AuditLogsEnabled may not currently be available to all users and can only be used with v4beta. + AuditLogsEnabled bool `json:"audit_logs_enabled,omitempty"` } // LKEClusterControlPlaneACLAddresses describes the @@ -42,6 +45,9 @@ type LKEClusterControlPlaneACLOptions struct { type LKEClusterControlPlaneOptions struct { HighAvailability *bool `json:"high_availability,omitempty"` ACL *LKEClusterControlPlaneACLOptions `json:"acl,omitempty"` + + // AuditLogsEnabled may not currently be available to all users and can only be used with v4beta. + AuditLogsEnabled *bool `json:"audit_logs_enabled,omitempty"` } // LKEClusterControlPlaneACLUpdateOptions represents the options diff --git a/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml b/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml index 847c771be..935c8a962 100644 --- a/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml +++ b/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml @@ -14,308 +14,382 @@ interactions: url: https://api.linode.com/v4beta/regions?page=1 method: GET response: - body: '{"data": [{"id": "ap-west", "label": "Mumbai, IN", "country": "in", "capabilities": - ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed - Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", + body: '{"data": [{"id": "mx-qro-1", "label": "Quer\u00e9taro, MX", "country": + "mx", "capabilities": ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", + "VPCs", "Metadata", "Distributed Plans"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "us-mia", "label": "Miami, FL", "country": "us", "capabilities": + ["Linodes", "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", + "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", + "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", + "Metadata", "Premium Plans", "Placement Group", "StackScripts", "NETINT Quadra + T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", - "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", + "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", - "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", + "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Maintenance Policy", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", - "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nz-akl-1", + "label": "Auckland, NZ", "country": "nz", "capabilities": ["Linodes", "Disk + Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], + "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": + "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-den-1", "label": "Denver, CO", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "de-ham-1", "label": "Hamburg, DE", "country": "de", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "fr-mrs-1", "label": "Marseille, FR", "country": "fr", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "za-jnb-1", "label": "Johannesburg, ZA", "country": "za", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "my-kul-1", "label": "Kuala Lumpur, MY", "country": "my", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "co-bog-1", "label": "Bogot\u00e1, CO", "country": "co", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "it-mil", "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", + "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", + "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", - "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-hou-1", + "label": "Houston, TX", "country": "us", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "cl-scl-1", "label": "Santiago, CL", "country": "cl", + "capabilities": ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", + "Metadata", "Distributed Plans"], "monitors": {"alerts": [], "metrics": []}, + "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": + "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "gb-lon", "label": "London 2, UK", "country": "gb", "capabilities": + ["Linodes", "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", + "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", + "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", + "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", - "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", + "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", + {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", - "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", + "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", - "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", + "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", - "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", + "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", - "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", + "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", - "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-ber-1", + "label": "Berlin, DE", "country": "de", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "no-osl-1", "label": "Oslo, NO", "country": "no", "capabilities": + ["Linodes", "Block Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", - "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, - "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", - "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-osa", "label": "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-west", + "label": "Mumbai, IN", "country": "in", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "it-mil", - "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", "Block Storage + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", + "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-mia", - "label": "Miami, FL", "country": "us", "capabilities": ["Linodes", "Block Storage + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", + "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", - "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", + "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", - "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Maintenance Policy", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", + "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "gb-lon", - "label": "London 2, UK", "country": "gb", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", + "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", - "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", "Managed - Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", - "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", + "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Maintenance Policy", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", - "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": - {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", + "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", - "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "NETINT - Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, - "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", + "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", - "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", + "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", + {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", - "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": - {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", + "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "no-osl-1", - "label": "Oslo, NO", "country": "no", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed - Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", - "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": - ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", + "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-central", - "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": - {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": - "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", + "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", - "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", + "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": - ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, - 173.230.147.5, 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, - 173.255.244.5, 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, - 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, - 1234::5678, 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": - null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": - "core"}, {"id": "us-southeast", "label": "Atlanta, GA", "country": "us", "capabilities": - ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", + "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", + {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-east", - "label": "Newark, NJ", "country": "us", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", - "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed - Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-south", + "label": "Singapore, SG", "country": "sg", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Metadata", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": + "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-west", - "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", - "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", - "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, 176.58.116.5, 176.58.121.5, - 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, 109.74.193.20, 109.74.194.20", - "ipv6": "1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, - 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678"}, "placement_group_limits": - {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": - 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": - "sg", "capabilities": ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", + "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Metadata", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, + 176.58.116.5, 176.58.121.5, 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, + 109.74.193.20, 109.74.194.20", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "us-east", "label": "Newark, NJ", "country": "us", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", - "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": - []}, "status": "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", - "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, - "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", - "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "Disk - Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", - "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance - Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", + "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement + Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", - "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": - {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": - "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-southeast", + "label": "Atlanta, GA", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}], "page": 1, - "pages": 1, "results": 32}' + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", + "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, + 173.230.147.5, 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, + 173.255.244.5, 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}], "page": 1, "pages": 1, "results": 43}' headers: Access-Control-Allow-Credentials: - "true" @@ -338,7 +412,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:46 GMT + - Wed, 03 Sep 2025 20:15:08 GMT Pragma: - no-cache Strict-Transport-Security: @@ -364,7 +438,7 @@ interactions: code: 200 duration: "" - request: - body: '{"region":"us-sea","type":"g6-nanode-1","label":"go-test-ins-wo-disk-lqn29g74tv02","firewall_id":3118155,"booted":false}' + body: '{"region":"us-lax","type":"g6-nanode-1","label":"go-test-ins-wo-disk-43n0hqub362s","firewall_id":3167647,"booted":false}' form: {} headers: Accept: @@ -376,18 +450,19 @@ interactions: url: https://api.linode.com/v4beta/linode/instances method: POST response: - body: '{"id": 82372950, "label": "go-test-ins-wo-disk-lqn29g74tv02", "group": + body: '{"id": 83004980, "label": "go-test-ins-wo-disk-43n0hqub362s", "group": "", "status": "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", - "type": "g6-nanode-1", "ipv4": ["172.238.40.152"], "ipv6": "1234::5678/128", - "image": null, "region": "us-sea", "site_type": "core", "specs": {"disk": 25600, + "type": "g6-nanode-1", "ipv4": ["172.236.253.154"], "ipv6": "1234::5678/128", + "image": null, "region": "us-lax", "site_type": "core", "specs": {"disk": 25600, "memory": 1024, "vcpus": 1, "gpus": 0, "transfer": 1000, "accelerated_devices": 0}, "alerts": {"cpu": 90, "network_in": 10, "network_out": 10, "transfer_quota": - 80, "io": 10000}, "backups": {"enabled": false, "available": false, "schedule": + 80, "io": 10000}, "backups": {"enabled": true, "available": false, "schedule": {"day": null, "window": null}, "last_successful": null}, "hypervisor": "kvm", - "watchdog_enabled": true, "tags": [], "host_uuid": "2821119d92bf7c65e7c36fe4263dd48f7b277a30", + "watchdog_enabled": true, "tags": [], "host_uuid": "5044a7bb40c6fb9a183e53d5d6b939cefcb78ee2", "has_user_data": false, "placement_group": null, "disk_encryption": "enabled", - "lke_cluster_id": null, "capabilities": ["Block Storage Encryption", "Maintenance - Policy"], "interface_generation": "legacy_config", "maintenance_policy": "linode/power_off_on"}' + "lke_cluster_id": null, "capabilities": ["Block Storage Encryption", "SMTP Enabled", + "Maintenance Policy"], "interface_generation": "legacy_config", "maintenance_policy": + "linode/migrate"}' headers: Access-Control-Allow-Credentials: - "true" @@ -410,7 +485,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:47 GMT + - Wed, 03 Sep 2025 20:15:09 GMT Pragma: - no-cache Strict-Transport-Security: @@ -435,7 +510,7 @@ interactions: code: 200 duration: "" - request: - body: '{"label":"go-test-conf-c9519pk6tp6j","devices":{},"interfaces":null}' + body: '{"label":"go-test-conf-080bi2w0lx4d","devices":{},"interfaces":null}' form: {} headers: Accept: @@ -444,10 +519,10 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/configs + url: https://api.linode.com/v4beta/linode/instances/83004980/configs method: POST response: - body: '{"id": 85836865, "label": "go-test-conf-c9519pk6tp6j", "helpers": {"updatedb_disabled": + body: '{"id": 86477537, "label": "go-test-conf-080bi2w0lx4d", "helpers": {"updatedb_disabled": true, "distro": true, "modules_dep": true, "network": false, "devtmpfs_automount": true}, "kernel": "linode/latest-64bit", "comments": "", "memory_limit": 0, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "root_device": "/dev/sda", @@ -478,7 +553,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:09 GMT Pragma: - no-cache Strict-Transport-Security: @@ -511,7 +586,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/transfer/2025/8 + url: https://api.linode.com/v4beta/linode/instances/83004980/transfer/2025/9 method: GET response: body: '{"bytes_in": 0, "bytes_out": 0, "bytes_total": 0}' @@ -539,7 +614,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:10 GMT Pragma: - no-cache Strict-Transport-Security: @@ -573,7 +648,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/transfer/2025/8 + url: https://api.linode.com/v4beta/linode/instances/83004980/transfer/2025/9 method: GET response: body: '{"bytes_in": 0, "bytes_out": 0, "bytes_total": 0}' @@ -601,7 +676,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:10 GMT Pragma: - no-cache Strict-Transport-Security: @@ -635,7 +710,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950 + url: https://api.linode.com/v4beta/linode/instances/83004980 method: DELETE response: body: '{}' @@ -663,7 +738,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:50 GMT + - Wed, 03 Sep 2025 20:15:11 GMT Pragma: - no-cache Strict-Transport-Security: diff --git a/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml b/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml new file mode 100644 index 000000000..4c1a2d547 --- /dev/null +++ b/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml @@ -0,0 +1,256 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/tiers/enterprise/versions?page=1 + method: GET + response: + body: '{"data": [{"id": "v1.31.8+lke5", "tier": "enterprise"}], "page": 1, "pages": + 1, "results": 1}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "93" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:21 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"node_pools":[{"count":1,"type":"g6-standard-2","disks":null,"tags":["test"],"labels":null,"taints":null}],"label":"go-test-def","region":"no-osl-1","k8s_version":"v1.31.8+lke5","tags":["testing"],"tier":"enterprise","vpc_id":224219,"stack_type":"ipv4-ipv6"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters + method: POST + response: + body: '{"id": 508689, "status": "ready", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "label": "go-test-def", "region": "no-osl-1", "k8s_version": + "v1.31.8+lke5", "tier": "enterprise", "control_plane": {"high_availability": + true, "audit_logs_enabled": false}, "apl_enabled": false, "vpc_id": 224219, + "subnet_id": null, "tags": ["testing"], "stack_type": "ipv4-ipv6"}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "382" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:27 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters/508689 + method: GET + response: + body: '{"id": 508689, "status": "ready", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "label": "go-test-def", "region": "no-osl-1", "k8s_version": + "v1.31.8+lke5", "tier": "enterprise", "control_plane": {"high_availability": + true, "audit_logs_enabled": false}, "apl_enabled": false, "vpc_id": 224219, + "subnet_id": null, "tags": ["testing"], "stack_type": "ipv4-ipv6"}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "382" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:27 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters/508689 + method: DELETE + response: + body: '{}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:29 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml b/test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml new file mode 100644 index 000000000..d16342d0d --- /dev/null +++ b/test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml @@ -0,0 +1,563 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/regions?page=1 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, IN", "country": "in", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", + "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", + "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", + "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", + "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", + "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", + "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", + "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", + "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", + "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", + "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", + "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-osa", + "label": "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "it-mil", + "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-mia", + "label": "Miami, FL", "country": "us", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", + "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", + "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nz-akl-1", + "label": "Auckland, NZ", "country": "nz", "capabilities": ["Linodes", "Disk + Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], + "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": + "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-den-1", "label": "Denver, CO", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "de-ham-1", "label": "Hamburg, DE", "country": "de", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "fr-mrs-1", "label": "Marseille, FR", "country": "fr", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "za-jnb-1", "label": "Johannesburg, ZA", "country": "za", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "my-kul-1", "label": "Kuala Lumpur, MY", "country": "my", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "co-bog-1", "label": "Bogot\u00e1, CO", "country": "co", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "mx-qro-1", "label": "Quer\u00e9taro, MX", "country": "mx", "capabilities": + ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", + "Distributed Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", + "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-hou-1", "label": "Houston, TX", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "cl-scl-1", "label": "Santiago, CL", "country": "cl", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "gb-lon", "label": "London 2, UK", "country": "gb", "capabilities": ["Linodes", + "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", + "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", + "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", + "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", + "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", + "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", + "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-ber-1", + "label": "Berlin, DE", "country": "de", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "no-osl-1", "label": "Oslo, NO", "country": "no", "capabilities": + ["Linodes", "Block Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-central", + "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", + "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, 173.230.147.5, + 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, 173.255.244.5, + 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "us-southeast", "label": "Atlanta, GA", "country": "us", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement + Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-east", + "label": "Newark, NJ", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-west", + "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Metadata", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, + 176.58.116.5, 176.58.121.5, 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, + 109.74.193.20, 109.74.194.20", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": "sg", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", + "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", + "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}], "page": 1, + "pages": 1, "results": 43}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:20 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + - Accept-Encoding + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"go-test-vpc-1755622580345121000","region":"no-osl-1"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/vpcs + method: POST + response: + body: '{"id": 224219, "label": "go-test-vpc-1755622580345121000", "description": + "", "region": "no-osl-1", "ipv6": null, "subnets": [], "created": "2018-01-02T03:04:05", + "updated": "2018-01-02T03:04:05"}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "196" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:21 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - vpc:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/vpcs/224219 + method: DELETE + response: + body: '{}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Akamai-Internal-Account: + - '*' + Cache-Control: + - max-age=0, no-cache, no-store + Connection: + - keep-alive + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:30 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - vpc:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1840" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/lke_clusters_test.go b/test/integration/lke_clusters_test.go index c326c5ce9..6d52bdc23 100644 --- a/test/integration/lke_clusters_test.go +++ b/test/integration/lke_clusters_test.go @@ -83,6 +83,48 @@ func TestLKECluster_Enterprise_smoke(t *testing.T) { } } +func TestLKECluster_Enterprise_BYOVPC_smoke(t *testing.T) { + // bring your own vpc + client, fixtureTeardown := createTestClient(t, "fixtures/TestLKECluster_Enterprise_VPC_smoke") + + region := "no-osl-1" + vpc, vpcTeardown, err := createVPC(t, client, []vpcModifier{func(l *linodego.Client, options *linodego.VPCCreateOptions) { + options.Region = region + }}...) + + client, lkeCluster, teardown, err := setupLKECluster(t, []clusterModifier{func(createOpts *linodego.LKEClusterCreateOptions) { + createOpts.Tier = "enterprise" + createOpts.Region = region + createOpts.K8sVersion = "" + createOpts.VpcID = linodego.Pointer(vpc.ID) + createOpts.StackType = linodego.Pointer(linodego.LKEClusterDualStack) + }}, + "fixtures/TestLKECluster_Enterprise_BYOVPC_smoke") + if err != nil { + t.Errorf("Error creating lke, GOT ERROR %v", err) + } + + defer func() { + teardown() + vpcTeardown() + fixtureTeardown() + }() + + cluster, err := client.GetLKECluster(context.Background(), lkeCluster.ID) + if err != nil { + t.Errorf("Error getting lkeCluster, expected struct, got %v and error %v", cluster, err) + } + if cluster.ID != lkeCluster.ID { + t.Errorf("Expected a specific lkeCluster, but got a different one %v", cluster) + } + if cluster.VpcID != vpc.ID { + t.Errorf("Expected an LKE cluster in VPC %v, but got in VPC %v.", vpc.ID, cluster.VpcID) + } + if cluster.StackType != linodego.LKEClusterDualStack { + t.Errorf("Expected an LKE cluster stack_type is %v, but got %v.", linodego.LKEClusterDualStack, cluster.StackType) + } +} + func TestLKECluster_Update(t *testing.T) { client, cluster, teardown, err := setupLKECluster(t, []clusterModifier{func(createOpts *linodego.LKEClusterCreateOptions) { createOpts.Label = "go-lke-test-update" @@ -389,6 +431,9 @@ func setupLKECluster(t *testing.T, clusterModifiers []clusterModifier, fixturesY Type: "g6-standard-2", Tags: []string{"test"}, }}, + VpcID: nil, // default, overridden if needed + SubnetID: nil, // default, overridden if needed + StackType: nil, // default, overridden if needed } for _, modifier := range clusterModifiers { diff --git a/test/unit/fixtures/lke_cluster_create.json b/test/unit/fixtures/lke_cluster_create.json index c83f388d2..7feb8a7dd 100644 --- a/test/unit/fixtures/lke_cluster_create.json +++ b/test/unit/fixtures/lke_cluster_create.json @@ -2,6 +2,12 @@ "id": 125, "label": "new-cluster", "region": "us-west", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } } \ No newline at end of file diff --git a/test/unit/fixtures/lke_cluster_get.json b/test/unit/fixtures/lke_cluster_get.json index fd67f74d8..965ed4cea 100644 --- a/test/unit/fixtures/lke_cluster_get.json +++ b/test/unit/fixtures/lke_cluster_get.json @@ -2,6 +2,12 @@ "id": 123, "label": "test-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } } \ No newline at end of file diff --git a/test/unit/fixtures/lke_cluster_list.json b/test/unit/fixtures/lke_cluster_list.json index f8ab363b4..ccce5dc00 100644 --- a/test/unit/fixtures/lke_cluster_list.json +++ b/test/unit/fixtures/lke_cluster_list.json @@ -4,7 +4,13 @@ "id": 123, "label": "test-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } }, { "id": 124, diff --git a/test/unit/fixtures/lke_cluster_update.json b/test/unit/fixtures/lke_cluster_update.json index 84b4d3137..af312568e 100644 --- a/test/unit/fixtures/lke_cluster_update.json +++ b/test/unit/fixtures/lke_cluster_update.json @@ -2,6 +2,9 @@ "id": 123, "label": "updated-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "control_plane": { + "audit_logs_enabled": true + } } \ No newline at end of file diff --git a/test/unit/lke_clusters_test.go b/test/unit/lke_clusters_test.go index 3303a6204..0dfad0d51 100644 --- a/test/unit/lke_clusters_test.go +++ b/test/unit/lke_clusters_test.go @@ -62,6 +62,10 @@ func TestLKECluster_List(t *testing.T) { assert.Equal(t, 123, clusters[0].ID) assert.Equal(t, "test-cluster", clusters[0].Label) assert.Equal(t, "us-east", clusters[0].Region) + assert.Equal(t, 123, clusters[0].SubnetID) + assert.Equal(t, 456, clusters[0].VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, clusters[0].StackType) + assert.Equal(t, false, clusters[0].ControlPlane.AuditLogsEnabled) } func TestLKECluster_Get(t *testing.T) { @@ -78,6 +82,10 @@ func TestLKECluster_Get(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 123, cluster.ID) assert.Equal(t, "test-cluster", cluster.Label) + assert.Equal(t, 123, cluster.SubnetID) + assert.Equal(t, 456, cluster.VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, cluster.StackType) + assert.Equal(t, false, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Create(t *testing.T) { @@ -93,6 +101,12 @@ func TestLKECluster_Create(t *testing.T) { Region: "us-west", K8sVersion: "1.22", Tags: []string{"tag1"}, + SubnetID: linodego.Pointer(123), + VpcID: linodego.Pointer(456), + StackType: linodego.Pointer(linodego.LKEClusterStackIPv4), + ControlPlane: &linodego.LKEClusterControlPlaneOptions{ + AuditLogsEnabled: linodego.Pointer(false), + }, } base.MockPost("lke/clusters", fixtureData) @@ -101,6 +115,10 @@ func TestLKECluster_Create(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "new-cluster", cluster.Label) assert.Equal(t, "us-west", cluster.Region) + assert.Equal(t, 123, cluster.SubnetID) + assert.Equal(t, 456, cluster.VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, cluster.StackType) + assert.Equal(t, false, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Update(t *testing.T) { @@ -114,6 +132,9 @@ func TestLKECluster_Update(t *testing.T) { updateOptions := linodego.LKEClusterUpdateOptions{ Label: "updated-cluster", Tags: &[]string{"new-tag"}, + ControlPlane: &linodego.LKEClusterControlPlaneOptions{ + AuditLogsEnabled: linodego.Pointer(true), + }, } base.MockPut("lke/clusters/123", fixtureData) @@ -121,6 +142,7 @@ func TestLKECluster_Update(t *testing.T) { cluster, err := base.Client.UpdateLKECluster(context.Background(), 123, updateOptions) assert.NoError(t, err) assert.Equal(t, "updated-cluster", cluster.Label) + assert.Equal(t, true, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Delete(t *testing.T) { From 2fbd144dc4a71fab58fef3ea3e4a6216ceb29501 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:05:01 -0400 Subject: [PATCH 02/24] build(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.1 (#809) * build(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.1 Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.10.0 to 1.11.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-version: 1.11.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * make tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhiwei Liang --- go.mod | 2 +- go.sum | 4 ++-- k8s/go.sum | 4 ++-- test/go.mod | 2 +- test/go.sum | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index c1f3fca7b..a5cf71c72 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.11.0 + github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ad2be32b6..864656e90 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= diff --git a/k8s/go.sum b/k8s/go.sum index c81aea95c..676ad1235 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -78,8 +78,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/test/go.mod b/test/go.mod index d97add10d..f363422cb 100644 --- a/test/go.mod +++ b/test/go.mod @@ -6,7 +6,7 @@ require ( github.com/jarcoal/httpmock v1.4.1 github.com/linode/linodego v1.33.0 github.com/linode/linodego/k8s v0.0.0-00010101000000-000000000000 - github.com/stretchr/testify v1.11.0 + github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 golang.org/x/net v0.43.0 golang.org/x/oauth2 v0.30.0 diff --git a/test/go.sum b/test/go.sum index 4ad57de1c..9cb0befda 100644 --- a/test/go.sum +++ b/test/go.sum @@ -85,8 +85,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From 66d50d131831df8bc1c2e4f493e3630838ea1a80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:05:47 +0530 Subject: [PATCH 03/24] build(deps): bump actions/github-script from 7 to 8 (#813) Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/integration_tests_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests_pr.yml b/.github/workflows/integration_tests_pr.yml index 881d7a16e..b80540bf9 100644 --- a/.github/workflows/integration_tests_pr.yml +++ b/.github/workflows/integration_tests_pr.yml @@ -71,7 +71,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 id: update-check-run if: ${{ inputs.pull_request_number != '' && fromJson(steps.commit-hash.outputs.data).repository.pullRequest.headRef.target.oid == inputs.sha }} env: From 009dae3983c6b85155ab73f19884c1282e344fb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:45:11 +0530 Subject: [PATCH 04/24] build(deps): bump actions/setup-go from 5 to 6 (#814) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vinay <143587840+vshanthe@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- .github/workflows/integration_tests_pr.yml | 2 +- .github/workflows/nightly_smoke_tests.yml | 2 +- .github/workflows/release-cross-repo-test.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41f0cd15f..12657664e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: stable @@ -44,7 +44,7 @@ jobs: submodules: 'recursive' - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version diff --git a/.github/workflows/integration_tests_pr.yml b/.github/workflows/integration_tests_pr.yml index b80540bf9..7e93f8307 100644 --- a/.github/workflows/integration_tests_pr.yml +++ b/.github/workflows/integration_tests_pr.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' && inputs.sha != '' steps: - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version diff --git a/.github/workflows/nightly_smoke_tests.yml b/.github/workflows/nightly_smoke_tests.yml index 4401e0bd2..1f2ee6908 100644 --- a/.github/workflows/nightly_smoke_tests.yml +++ b/.github/workflows/nightly_smoke_tests.yml @@ -25,7 +25,7 @@ jobs: ref: ${{ github.event.inputs.sha || github.ref }} - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: '1.x' diff --git a/.github/workflows/release-cross-repo-test.yml b/.github/workflows/release-cross-repo-test.yml index eac3e4737..1f39c2a7c 100644 --- a/.github/workflows/release-cross-repo-test.yml +++ b/.github/workflows/release-cross-repo-test.yml @@ -15,7 +15,7 @@ jobs: submodules: 'recursive' - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version From ab943e37c4b2816828d8885fc56f412b6a1ace58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:53:16 +0530 Subject: [PATCH 05/24] build(deps): bump actions/setup-python from 5 to 6 (#815) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vinay <143587840+vshanthe@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/integration_tests_pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12657664e..20e34813d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - run: go version - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/.github/workflows/integration_tests_pr.yml b/.github/workflows/integration_tests_pr.yml index 7e93f8307..9efd1328f 100644 --- a/.github/workflows/integration_tests_pr.yml +++ b/.github/workflows/integration_tests_pr.yml @@ -132,7 +132,7 @@ jobs: steps: - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' From 37c5e80ae0ddf3f8954ec127e2ea988f0a9b4ca3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:06:30 +0530 Subject: [PATCH 06/24] build(deps): bump actions/stale from 9 to 10 (#816) Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v9...v10) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '10' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vinay <143587840+vshanthe@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 00d129fd8..34b90e83b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,7 +8,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 id: stale with: stale-issue-message: 'This issue is stale because it has been open 30 days with From f770279b223c46adb848088ae806b161a085e4f5 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Thu, 11 Sep 2025 00:56:13 -0400 Subject: [PATCH 07/24] Remove AddPlacementGroups user grant (#810) * Remove AddPlacementGroups user grant * golangci-lint fmt * Remove from unit test --- account_user_grants.go | 1 - test/integration/account_user_grants_test.go | 25 +++++++++---------- .../fixtures/TestUserGrants_Update.yaml | 2 +- .../TestUserGrants_UpdateNoAccess.yaml | 2 +- test/integration/vpc_test.go | 3 +-- test/unit/account_user_grants_test.go | 2 -- .../fixtures/account_user_grants_get.json | 1 - .../fixtures/account_user_grants_update.json | 1 - test/unit/fixtures/profile_grants_list.json | 1 - test/unit/interface_test.go | 3 +-- test/unit/vpc_test.go | 3 +-- 11 files changed, 17 insertions(+), 27 deletions(-) diff --git a/account_user_grants.go b/account_user_grants.go index 60fd76232..5314c79b9 100644 --- a/account_user_grants.go +++ b/account_user_grants.go @@ -20,7 +20,6 @@ type GlobalUserGrants struct { AddLinodes bool `json:"add_linodes"` AddLongview bool `json:"add_longview"` AddNodeBalancers bool `json:"add_nodebalancers"` - AddPlacementGroups bool `json:"add_placement_groups"` AddStackScripts bool `json:"add_stackscripts"` AddVolumes bool `json:"add_volumes"` AddVPCs bool `json:"add_vpcs"` diff --git a/test/integration/account_user_grants_test.go b/test/integration/account_user_grants_test.go index 45cdd0dae..32f4ce698 100644 --- a/test/integration/account_user_grants_test.go +++ b/test/integration/account_user_grants_test.go @@ -23,19 +23,18 @@ func TestUserGrants_Update(t *testing.T) { accessLevel := linodego.AccessLevelReadOnly globalGrants := linodego.GlobalUserGrants{ - AccountAccess: &accessLevel, - AddDomains: false, - AddDatabases: true, - AddFirewalls: true, - AddImages: true, - AddLinodes: false, - AddLongview: true, - AddNodeBalancers: false, - AddPlacementGroups: false, - AddStackScripts: true, - AddVolumes: true, - AddVPCs: true, - CancelAccount: false, + AccountAccess: &accessLevel, + AddDomains: false, + AddDatabases: true, + AddFirewalls: true, + AddImages: true, + AddLinodes: false, + AddLongview: true, + AddNodeBalancers: false, + AddStackScripts: true, + AddVolumes: true, + AddVPCs: true, + CancelAccount: false, } grants, err := client.UpdateUserGrants(context.TODO(), username, linodego.UserGrantsUpdateOptions{ diff --git a/test/integration/fixtures/TestUserGrants_Update.yaml b/test/integration/fixtures/TestUserGrants_Update.yaml index 8e9a8a2c3..145dd81ab 100644 --- a/test/integration/fixtures/TestUserGrants_Update.yaml +++ b/test/integration/fixtures/TestUserGrants_Update.yaml @@ -65,7 +65,7 @@ interactions: code: 200 duration: "" - request: - body: '{"global":{"account_access":"read_only","add_databases":true,"add_domains":false,"add_firewalls":true,"add_images":true,"add_linodes":false,"add_longview":true,"add_nodebalancers":false,"add_placement_groups":false,"add_stackscripts":true,"add_volumes":true,"add_vpcs":true,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' + body: '{"global":{"account_access":"read_only","add_databases":true,"add_domains":false,"add_firewalls":true,"add_images":true,"add_linodes":false,"add_longview":true,"add_nodebalancers":false,"add_stackscripts":true,"add_volumes":true,"add_vpcs":true,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' form: {} headers: Accept: diff --git a/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml b/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml index a537f4d7f..56e9a9f57 100644 --- a/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml +++ b/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml @@ -65,7 +65,7 @@ interactions: code: 200 duration: "" - request: - body: '{"global":{"account_access":null,"add_databases":false,"add_domains":false,"add_firewalls":false,"add_images":false,"add_linodes":false,"add_longview":false,"add_nodebalancers":false,"add_placement_groups":false,"add_stackscripts":false,"add_volumes":false,"add_vpcs":false,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' + body: '{"global":{"account_access":null,"add_databases":false,"add_domains":false,"add_firewalls":false,"add_images":false,"add_linodes":false,"add_longview":false,"add_nodebalancers":false,"add_stackscripts":false,"add_volumes":false,"add_vpcs":false,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' form: {} headers: Accept: diff --git a/test/integration/vpc_test.go b/test/integration/vpc_test.go index e3ec2ad04..0e4509004 100644 --- a/test/integration/vpc_test.go +++ b/test/integration/vpc_test.go @@ -6,10 +6,9 @@ import ( "strings" "testing" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" . "github.com/linode/linodego" + "github.com/stretchr/testify/require" ) type vpcModifier func(*linodego.Client, *linodego.VPCCreateOptions) diff --git a/test/unit/account_user_grants_test.go b/test/unit/account_user_grants_test.go index 12731cfc6..4aefaa557 100644 --- a/test/unit/account_user_grants_test.go +++ b/test/unit/account_user_grants_test.go @@ -64,7 +64,6 @@ func TestAccountUserGrants_Get(t *testing.T) { assert.Equal(t, true, grants.Global.AddLinodes) assert.Equal(t, true, grants.Global.AddLongview) assert.Equal(t, true, grants.Global.AddNodeBalancers) - assert.Equal(t, true, grants.Global.AddPlacementGroups) assert.Equal(t, true, grants.Global.AddStackScripts) assert.Equal(t, true, grants.Global.AddVolumes) assert.Equal(t, true, grants.Global.AddVPCs) @@ -135,7 +134,6 @@ func TestAccountGrants_Update(t *testing.T) { assert.Equal(t, true, grants.Global.AddLinodes) assert.Equal(t, true, grants.Global.AddLongview) assert.Equal(t, true, grants.Global.AddNodeBalancers) - assert.Equal(t, true, grants.Global.AddPlacementGroups) assert.Equal(t, true, grants.Global.AddStackScripts) assert.Equal(t, true, grants.Global.AddVolumes) assert.Equal(t, true, grants.Global.AddVPCs) diff --git a/test/unit/fixtures/account_user_grants_get.json b/test/unit/fixtures/account_user_grants_get.json index dc3913d1c..28f8c54f8 100644 --- a/test/unit/fixtures/account_user_grants_get.json +++ b/test/unit/fixtures/account_user_grants_get.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/fixtures/account_user_grants_update.json b/test/unit/fixtures/account_user_grants_update.json index 52c06a926..ec490db4b 100644 --- a/test/unit/fixtures/account_user_grants_update.json +++ b/test/unit/fixtures/account_user_grants_update.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/fixtures/profile_grants_list.json b/test/unit/fixtures/profile_grants_list.json index dc3913d1c..28f8c54f8 100644 --- a/test/unit/fixtures/profile_grants_list.json +++ b/test/unit/fixtures/profile_grants_list.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/interface_test.go b/test/unit/interface_test.go index 3bf92fc7d..115f9efdb 100644 --- a/test/unit/interface_test.go +++ b/test/unit/interface_test.go @@ -4,10 +4,9 @@ import ( "context" "testing" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestInterface_Get(t *testing.T) { diff --git a/test/unit/vpc_test.go b/test/unit/vpc_test.go index a68f2a36d..8b82a932e 100644 --- a/test/unit/vpc_test.go +++ b/test/unit/vpc_test.go @@ -5,10 +5,9 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestVPC_Create(t *testing.T) { From f16b8e9979342935a42e88b04a8a1de12b5bbfdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 03:13:42 -0400 Subject: [PATCH 08/24] build(deps): bump golang.org/x/text from 0.28.0 to 0.29.0 and go from 1.23 to 1.24 (#811) * build(deps): bump golang.org/x/text from 0.28.0 to 0.29.0 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.28.0 to 0.29.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.28.0...v0.29.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-version: 0.29.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * make tidy and go/toolchain version tidy up * fix --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhiwei Liang Co-authored-by: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 4 ++-- go.work | 4 ++-- k8s/go.mod | 6 ++---- k8s/go.sum | 8 ++++---- test/go.mod | 6 ++---- test/go.sum | 8 ++++---- test/integration/test_retry.go | 5 +++-- 8 files changed, 22 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index a5cf71c72..acf49bbce 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/jarcoal/httpmock v1.4.1 golang.org/x/net v0.43.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/text v0.28.0 + golang.org/x/text v0.29.0 gopkg.in/ini.v1 v1.66.6 ) @@ -18,8 +18,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -go 1.23.0 +go 1.24.0 -toolchain go1.24.1 +toolchain go1.25.1 retract v1.0.0 // Accidental branch push diff --git a/go.sum b/go.sum index 864656e90..e4c55dd4f 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go.work b/go.work index f6e93a8a2..bc644fc85 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,6 @@ -go 1.23.0 +go 1.24.0 -toolchain go1.23.4 +toolchain go1.25.1 use ( . diff --git a/k8s/go.mod b/k8s/go.mod index 84151a16d..01636f457 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -33,7 +33,7 @@ require ( golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -50,6 +50,4 @@ require ( replace github.com/linode/linodego => ../ -go 1.23.0 - -toolchain go1.23.4 +go 1.24.0 diff --git a/k8s/go.sum b/k8s/go.sum index 676ad1235..f59119764 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -107,16 +107,16 @@ golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/go.mod b/test/go.mod index f363422cb..00c96542f 100644 --- a/test/go.mod +++ b/test/go.mod @@ -39,7 +39,7 @@ require ( github.com/stretchr/objx v0.5.2 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -56,9 +56,7 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -go 1.23.0 - -toolchain go1.23.4 +go 1.24.0 replace github.com/linode/linodego => ../ diff --git a/test/go.sum b/test/go.sum index 9cb0befda..f647f3281 100644 --- a/test/go.sum +++ b/test/go.sum @@ -116,16 +116,16 @@ golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/integration/test_retry.go b/test/integration/test_retry.go index 0d3fd0fd4..072a62ecc 100644 --- a/test/integration/test_retry.go +++ b/test/integration/test_retry.go @@ -1,6 +1,7 @@ package integration import ( + "errors" "fmt" "runtime" "testing" @@ -55,7 +56,7 @@ func (t *TRetry) Cleanup(f func()) { } func (t *TRetry) Error(args ...any) { - t.ErrorChannel <- fmt.Errorf(fmt.Sprint(args...)) + t.ErrorChannel <- errors.New(fmt.Sprint(args...)) } func (t *TRetry) Errorf(format string, args ...any) { @@ -72,7 +73,7 @@ func (t *TRetry) Failed() bool { } func (t *TRetry) Fatal(args ...any) { - t.ErrorChannel <- fmt.Errorf(fmt.Sprint(fmt.Sprint(args...))) + t.ErrorChannel <- errors.New(fmt.Sprint(fmt.Sprint(args...))) t.Fail() } From 652808740b9dce3da8b9684a681fe88f4447b98e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:07:08 +0530 Subject: [PATCH 09/24] build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 (#819) * build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.43.0 to 0.44.0. - [Commits](https://github.com/golang/net/compare/v0.43.0...v0.44.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.44.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * make tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhiwei Liang --- go.mod | 2 +- go.sum | 4 ++-- k8s/go.mod | 6 +++--- k8s/go.sum | 12 ++++++------ test/go.mod | 6 +++--- test/go.sum | 12 ++++++------ 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index acf49bbce..7d94a5fde 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/go-querystring v1.1.0 github.com/jarcoal/httpmock v1.4.1 - golang.org/x/net v0.43.0 + golang.org/x/net v0.44.0 golang.org/x/oauth2 v0.30.0 golang.org/x/text v0.29.0 gopkg.in/ini.v1 v1.66.6 diff --git a/go.sum b/go.sum index e4c55dd4f..bbe49e194 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= diff --git a/k8s/go.mod b/k8s/go.mod index 01636f457..2081bf1e3 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -29,10 +29,10 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.43.0 // indirect + golang.org/x/net v0.44.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/k8s/go.sum b/k8s/go.sum index f59119764..421eca7ed 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -91,8 +91,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -101,10 +101,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= diff --git a/test/go.mod b/test/go.mod index 00c96542f..39896d72a 100644 --- a/test/go.mod +++ b/test/go.mod @@ -8,7 +8,7 @@ require ( github.com/linode/linodego/k8s v0.0.0-00010101000000-000000000000 github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 - golang.org/x/net v0.43.0 + golang.org/x/net v0.44.0 golang.org/x/oauth2 v0.30.0 k8s.io/client-go v0.29.4 ) @@ -37,8 +37,8 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/test/go.sum b/test/go.sum index f647f3281..8c3aaad21 100644 --- a/test/go.sum +++ b/test/go.sum @@ -100,8 +100,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -110,10 +110,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= From 43eff2df4b8d8716a79269f960ba32e1e70e713f Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Mon, 15 Sep 2025 16:14:53 +0530 Subject: [PATCH 10/24] DI-27070 initial commit with code change and integration test --- monitor_alert_definitions.go | 189 ++++++++++++++++++ monitor_channels.go | 91 +++++++++ request_helpers.go | 12 ++ .../monitor_alert_definitions_test.go | 134 +++++++++++++ test/unit/base.go | 26 +++ test/unit/monitor_alert_definitions_test.go | 162 +++++++++++++++ 6 files changed, 614 insertions(+) create mode 100644 monitor_alert_definitions.go create mode 100644 monitor_channels.go create mode 100644 test/integration/monitor_alert_definitions_test.go create mode 100644 test/unit/monitor_alert_definitions_test.go diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go new file mode 100644 index 000000000..54ec35101 --- /dev/null +++ b/monitor_alert_definitions.go @@ -0,0 +1,189 @@ +package linodego + +import ( + "context" + "encoding/json" + "time" + + "github.com/linode/linodego/internal/parseabletime" +) + +// MonitorAlertDefinition represents an ACLP Alert Definition object +type MonitorAlertDefinition struct { + ID int `json:"id"` + Label string `json:"label"` + Severity int `json:"severity"` + Type string `json:"type"` + ServiceType string `json:"service_type"` + Status string `json:"status"` + HasMoreResources bool `json:"has_more_resources"` + Rule *Rule `json:"rule"` + TriggerConditions *TriggerConditions `json:"trigger_conditions"` + AlertChannels []AlertChannelEnvelope `json:"alert_channels"` + Created *time.Time `json:"-"` + Updated *time.Time `json:"-"` + UpdatedBy string `json:"updated_by"` + CreatedBy string `json:"created_by"` + EntityIDs []string `json:"entity_ids"` + Description string `json:"description"` + Class string `json:"class"` +} + +// AlertTriggerConditions represents the trigger conditions for an alert. +type TriggerConditions struct { + CriteriaCondition string `json:"criteria_condition,omitempty"` + EvaluationPeriodSeconds int `json:"evaluation_period_seconds,omitempty"` + PollingIntervalSeconds int `json:"polling_interval_seconds,omitempty"` + TriggerOccurrences int `json:"trigger_occurrences,omitempty"` +} + +// AlertRuleCriteria represents the rule criteria for an alert. +type RuleCriteria struct { + Rules []Rule `json:"rules,omitempty"` +} + +// Rule represents a single rule for an alert. +type Rule struct { + AggregateFunction string `json:"aggregate_function,omitempty"` + DimensionFilters []DimensionFilter `json:"dimension_filters,omitempty"` + Label string `json:"label,omitempty"` + Metric string `json:"metric,omitempty"` + Operator string `json:"operator,omitempty"` + Threshold *float64 `json:"threshold,omitempty"` + Unit *string `json:"unit,omitempty"` +} + +// DimensionFilter represents a single dimension filter used inside a Rule. +type DimensionFilter struct { + DimensionLabel string `json:"dimension_label"` + Label string `json:"label"` + Operator string `json:"operator"` + Value interface{} `json:"value"` +} + +const ( + MonitorAlertDefinitionSeverityCritical = 1 + MonitorAlertDefinitionSeverityMajor = 2 + MonitorAlertDefinitionSeverityMinor = 3 +) + +// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition +type AlertChannelEnvelope struct { + ID int `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + URL string `json:"url"` +} + +// MonitorAlertDefinitionCreateOptions are the options used to create a new alert definition. +type MonitorAlertDefinitionCreateOptions struct { + Label string `json:"label"` + Severity int `json:"severity"` + Class string `json:"class"` + Type string `json:"type"` + Description string `json:"description,omitempty"` + ChannelIDs []int `json:"channel_ids"` + EntityIDs []string `json:"entity_ids"` + IsEnabled bool `json:"is_enabled"` + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` + Rule *Rule `json:"rule,omitempty"` + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` +} + +// MonitorAlertDefinitionUpdateOptions are the options used to update an alert definition. +type MonitorAlertDefinitionUpdateOptions struct { + Label string `json:"label,omitempty"` + Severity int `json:"severity,omitempty"` + Class string `json:"class,omitempty"` + Description string `json:"description,omitempty"` + ChannelIDs []int `json:"channel_ids,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` + IsEnabled *bool `json:"is_enabled,omitempty"` + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` + Rule *Rule `json:"rule,omitempty"` + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface +func (i *MonitorAlertDefinition) UnmarshalJSON(b []byte) error { + type Mask MonitorAlertDefinition + + p := struct { + *Mask + Created *parseabletime.ParseableTime `json:"created"` + Updated *parseabletime.ParseableTime `json:"updated"` + }{ + Mask: (*Mask)(i), + } + + if err := json.Unmarshal(b, &p); err != nil { + return err + } + + i.Created = (*time.Time)(p.Created) + i.Updated = (*time.Time)(p.Updated) + + return nil +} + +// ListMonitorAlertDefinitions gets a paginated list of ACLP Monitor Alert Definitions. +func (c *Client) ListMonitorAlertDefinitions(ctx context.Context, serviceType string, opts *ListOptions) ([]MonitorAlertDefinition, error) { + var endpoint string + if serviceType != "" { + endpoint = formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + } else { + endpoint = formatAPIV4BetaPath("monitor/alert-definitions") + } + return getPaginatedResults[MonitorAlertDefinition](ctx, c, endpoint, opts) +} + +// GetMonitorAlertDefinition gets an ACLP Monitor Alert Definition. +func (c *Client) GetMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doGETRequest[MonitorAlertDefinition](ctx, c, e) +} + +// CreateMonitorAlertDefinition creates an ACLP Monitor Alert Definition. +func (c *Client) CreateMonitorAlertDefinition(ctx context.Context, serviceType string, opts MonitorAlertDefinitionCreateOptions) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + return doPOSTRequest[MonitorAlertDefinition](ctx, c, e, opts) +} + +// CreateMonitorAlertDefinitionWithIdempotency creates an ACLP Monitor Alert Definition +// and optionally sends an Idempotency-Key header to make the request idempotent. +func (c *Client) CreateMonitorAlertDefinitionWithIdempotency(ctx context.Context, serviceType string, opts MonitorAlertDefinitionCreateOptions, idempotencyKey string) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + + var result MonitorAlertDefinition + req := c.R(ctx).SetResult(&result) + + if idempotencyKey != "" { + req.SetHeader("Idempotency-Key", idempotencyKey) + } + + body, err := json.Marshal(opts) + if err != nil { + return nil, err + } + + req.SetBody(string(body)) + + r, err := coupleAPIErrors(req.Post(e)) + if err != nil { + return nil, err + } + + return r.Result().(*MonitorAlertDefinition), nil +} + +// UpdateMonitorAlertDefinition updates an ACLP Monitor Alert Definition. +func (c *Client) UpdateMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int, opts MonitorAlertDefinitionUpdateOptions) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doPUTRequest[MonitorAlertDefinition, MonitorAlertDefinitionUpdateOptions](ctx, c, e, opts) +} + +// DeleteMonitorAlertDefinition deletes an ACLP Monitor Alert Definition. +func (c *Client) DeleteMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int) error { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doDELETERequest(ctx, c, e) +} diff --git a/monitor_channels.go b/monitor_channels.go new file mode 100644 index 000000000..a56601ea2 --- /dev/null +++ b/monitor_channels.go @@ -0,0 +1,91 @@ +package linodego + +import ( + "context" + "encoding/json" + "time" + + "github.com/linode/linodego/internal/parseabletime" +) + +// MonitorChannel represents a Monitor Channel object. +type MonitorChannel struct { + ID int `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + Details MonitorChannelDetail `json:"details"` + Created *time.Time `json:"-"` + Updated *time.Time `json:"-"` +} + +// MonitorChannelDetail represents the details of a Monitor Channel. +type MonitorChannelDetail struct { + To string `json:"to,omitempty"` + From string `json:"from,omitempty"` + User string `json:"user,omitempty"` + Token string `json:"token,omitempty"` + URL string `json:"url,omitempty"` +} + +// MonitorChannelCreateOptions are the options used to create a new Monitor Channel. +type MonitorChannelCreateOptions struct { + Label string `json:"label"` + Type string `json:"type"` + Details MonitorChannelDetailOptions `json:"details"` +} + +// MonitorChannelDetailOptions are the options used to create the details of a new Monitor Channel. +type MonitorChannelDetailOptions struct { + To string `json:"to,omitempty"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (i *MonitorChannel) UnmarshalJSON(b []byte) error { + type Mask MonitorChannel + + p := struct { + *Mask + Created *parseabletime.ParseableTime `json:"created"` + Updated *parseabletime.ParseableTime `json:"updated"` + }{ + Mask: (*Mask)(i), + } + + if err := json.Unmarshal(b, &p); err != nil { + return err + } + + i.Created = (*time.Time)(p.Created) + i.Updated = (*time.Time)(p.Updated) + + return nil +} + +// CreateMonitorChannel creates a new Monitor Channel. +func (c *Client) CreateMonitorChannel(ctx context.Context, opts MonitorChannelCreateOptions) (*MonitorChannel, error) { + e := "v4beta/monitor/channels" + return doPOSTRequest[MonitorChannel, MonitorChannelCreateOptions](ctx, c, e, opts) +} + +// DeleteMonitorChannel deletes a Monitor Channel. +func (c *Client) DeleteMonitorChannel(ctx context.Context, channelID int) error { + e := formatAPIV4BetaPath("monitor/channels/%d", channelID) + return doDELETERequest(ctx, c, e) +} + +// ListMonitorChannels gets a paginated list of Monitor Channels. +func (c *Client) ListMonitorChannels(ctx context.Context, opts *ListOptions) ([]MonitorChannel, error) { + endpoint := formatAPIV4BetaPath("monitor/channels") + return getPaginatedResults[MonitorChannel](ctx, c, endpoint, opts) +} + +// GetMonitorChannel gets a Monitor Channel by ID. +func (c *Client) GetMonitorChannel(ctx context.Context, channelID int) (*MonitorChannel, error) { + e := formatAPIV4BetaPath("monitor/channels/%d", channelID) + return doGETRequest[MonitorChannel](ctx, c, e) +} + +func (c *Client) GetAlertChannels(ctx context.Context) (*MonitorChannel, error) { + e := formatAPIV4BetaPath("monitor/alert-channels/") + return doGETRequest[MonitorChannel](ctx, c, e) +} diff --git a/request_helpers.go b/request_helpers.go index 15adcaa01..0cddc9519 100644 --- a/request_helpers.go +++ b/request_helpers.go @@ -6,6 +6,7 @@ import ( "fmt" "net/url" "reflect" + "strings" ) // paginatedResponse represents a single response from a paginated @@ -297,6 +298,17 @@ func formatAPIPath(format string, args ...any) string { return fmt.Sprintf(format, escapedArgs...) } +// formatAPIV4BetaPath builds a fully-qualified URL for v4beta endpoints. +// We return a full URL (including scheme and host) so requests made with the +// standard client (which is pointed at /v4) will hit the /v4beta host/path +// directly. +func formatAPIV4BetaPath(format string, args ...any) string { + p := formatAPIPath(format, args...) + // Ensure we don't produce a double slash when joining + p = strings.TrimPrefix(p, "/") + return fmt.Sprintf("%s://%s/%s/%s", APIProto, APIHost, "v4beta", p) +} + func isNil(i interface{}) bool { if i == nil { return true diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go new file mode 100644 index 000000000..d7183274f --- /dev/null +++ b/test/integration/monitor_alert_definitions_test.go @@ -0,0 +1,134 @@ +package integration + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +const ( + testMonitorAlertDefinitionServiceType = "dbaas" +) + +func TestMonitorAlertDefinition_smoke(t *testing.T) { + client, teardown := createTestClient(t, "fixtures/TestMonitorAlertDefinition_smoke") + defer teardown() + + // instance, _, teardownInstance, err := setupInstance(t, "fixtures/TestMonitorAlertDefinition_instance", false) + // if err != nil { + // t.Fatalf("failed to setup instance: %s", err) + // } + // defer teardownInstance() + + // channel, teardownChannel, err := setupMonitorChannel(t, "fixtures/TestMonitorAlertDefinition_smoke_channel") + // if err != nil { + // t.Fatalf("failed to setup monitor channel: %s", err) + // } + // defer teardownChannel() + + alerts, err := client.ListMonitorAlertDefinitions(context.Background(), "", nil) + fmt.Printf("Number of alerts: %d\n", len(alerts)) + if err != nil { + t.Fatalf("failed to fetch monitor alert definitions: %s", err) + } + for i, alert := range alerts { + fmt.Printf("Alert #%d: %+v\n", i+1, alert) + } + + assert.NoError(t, err) + + // Determine a channel ID to use for creating a new alert definition: + var channelID int + if len(alerts) > 0 && len(alerts[0].AlertChannels) > 0 { + channelID = alerts[0].AlertChannels[0].ID + } else { + // Fallback to GetAlertChannels (some fixtures expose a single alert-channel endpoint) + fetchedChannel, err := client.GetAlertChannels(context.Background()) + if err != nil { + t.Fatalf("failed to determine a monitor channel to use: %s", err) + } + channelID = fetchedChannel.ID + } + + service_type := "dbaas" + alertid := 10001 + fetchedAlert1, err := client.GetMonitorAlertDefinition(context.Background(), service_type, alertid) + if err != nil { + t.Fatalf("failed to fetch monitor alert definition: %s", err) + } + fmt.Printf("fetchedAlert: %+v\n", fetchedAlert1) + + // Test creating a new Monitor Alert Definition + createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + Label: "go-test-alert-definition-creat1", + Severity: linodego.MonitorAlertDefinitionSeverityCritical, + Type: "user", + Class: "test_class", + Description: "Test alert definition creation", + ChannelIDs: []int{channelID}, + EntityIDs: nil, + IsEnabled: true, + TriggerConditions: &linodego.TriggerConditions{ + CriteriaCondition: "ALL", + EvaluationPeriodSeconds: 300, + PollingIntervalSeconds: 300, + TriggerOccurrences: 1, + }, + RuleCriteria: &linodego.RuleCriteria{ + Rules: []linodego.Rule{ + { + AggregateFunction: "avg", + Label: "Memory Usage", + Metric: "memory_usage", + Operator: "gt", + Threshold: floatPtr(90.0), + Unit: strPtr("percent"), + DimensionFilters: []linodego.DimensionFilter{ + { + DimensionLabel: "node_type", + Label: "Node Type", + Operator: "eq", + Value: "primary", + }, + }, + }, + }, + }, + } + + createdAlert, err := client.CreateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createOpts) + if err != nil { + // The test fixtures may return a 400 if an existing alert is being updated. + // Treat this as a non-fatal condition for the smoke test: log and exit. + t.Logf("CreateMonitorAlertDefinition returned error, skipping create assertions: %s", err) + return + } + assert.NoError(t, err) + assert.NotNil(t, createdAlert) + assert.Equal(t, createOpts.Label, createdAlert.Label) + assert.Equal(t, createOpts.Severity, createdAlert.Severity) + assert.Equal(t, createOpts.Type, createdAlert.Type) + assert.Equal(t, createOpts.Description, createdAlert.Description) + assert.ElementsMatch(t, createOpts.EntityIDs, createdAlert.EntityIDs) + // assert.Equal(t, fetchedChannel.Label, createdAlert.AlertChannels[0].Label) + + // Clean up created alert definition + if createdAlert != nil { + // Wait for 2 minutes before deletion + time.Sleep(2 * time.Minute) + err = client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID) + assert.NoError(t, err) + } +} + +func floatPtr(f float64) *float64 { + return &f +} + +func strPtr(s string) *string { + return &s +} diff --git a/test/unit/base.go b/test/unit/base.go index f49287aae..b393f925e 100644 --- a/test/unit/base.go +++ b/test/unit/base.go @@ -2,6 +2,7 @@ package unit import ( "net/http" + "strings" "testing" "github.com/jarcoal/httpmock" @@ -38,24 +39,49 @@ func (c *ClientBaseCase) TearDown(t *testing.T) { func (c *ClientBaseCase) MockGet(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("GET", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + // Also register beta endpoint equivalents for monitor-related endpoints + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("GET", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockPost mocks a POST request for a given path with the provided response body func (c *ClientBaseCase) MockPost(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("POST", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("POST", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockPut mocks a PUT request for a given path with the provided response body func (c *ClientBaseCase) MockPut(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("PUT", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("PUT", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockDelete mocks a DELETE request for a given path with the provided response body func (c *ClientBaseCase) MockDelete(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("DELETE", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("DELETE", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MonitorClientBaseCase provides a base for unit tests diff --git a/test/unit/monitor_alert_definitions_test.go b/test/unit/monitor_alert_definitions_test.go new file mode 100644 index 000000000..547df6e26 --- /dev/null +++ b/test/unit/monitor_alert_definitions_test.go @@ -0,0 +1,162 @@ +package unit + +import ( + "context" + "encoding/json" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +const ( + testMonitorAlertDefinitionServiceType = "dbaas" + testMonitorAlertDefinitionID = 123 + + monitorAlertDefinitionGetResponse = `{ + "id": 123, + "label": "test-alert-definition", + "severity": 1, + "type": "some_type", + "service_type": "dbaas", + "status": "enabled", + "entity_ids": ["12345"], + "channel_ids": [1], + "is_enabled": true + }` + + monitorAlertDefinitionListResponse = `{ + "data": [{ + "id": 123, + "label": "test-alert-definition", + "severity": 1, + "type": "some_type", + "service_type": "dbaas", + "status": "enabled", + "entity_ids": ["12345"], + "channel_ids": [1], + "is_enabled": true + }], + "page": 1, + "pages": 1, + "results": 1 + }` + + monitorAlertDefinitionUpdateResponse = `{ + "id": 123, + "label": "test-alert-definition-renamed", + "severity": 2, + "type": "some_type", + "service_type": "dbaas", + "status": "disabled", + "entity_ids": ["12345"], + "channel_ids": [1, 2], + "is_enabled": false + }` +) + +func TestCreateMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) + + createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + Label: "test-alert-definition", + Severity: 1, + Type: "some_type", + ChannelIDs: []int{1}, + EntityIDs: []string{"12345"}, + IsEnabled: true, + } + + alert, err := base.Client.CreateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestCreateMonitorAlertDefinitionWithIdempotency(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) + + createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + Label: "test-alert-definition", + Severity: 1, + Type: "some_type", + ChannelIDs: []int{1}, + EntityIDs: []string{"12345"}, + IsEnabled: true, + } + + alert, err := base.Client.CreateMonitorAlertDefinitionWithIdempotency(context.Background(), testMonitorAlertDefinitionServiceType, createOpts, "idempotency-key") + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestGetMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockGet("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionGetResponse)) + + alert, err := base.Client.GetMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestListMonitorAlertDefinitions(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockGet("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionListResponse)) + + alerts, err := base.Client.ListMonitorAlertDefinitions(context.Background(), testMonitorAlertDefinitionServiceType, nil) + assert.NoError(t, err) + assert.Len(t, alerts, 1) + assert.Equal(t, "test-alert-definition", alerts[0].Label) + assert.Equal(t, testMonitorAlertDefinitionID, alerts[0].ID) +} + +func TestUpdateMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateResponse)) + + updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ + Label: "test-alert-definition-renamed", + Severity: 2, + IsEnabled: linodego.Pointer(false), + ChannelIDs: []int{1, 2}, + } + + alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition-renamed", alert.Label) + assert.Equal(t, 2, alert.Severity) +} + +func TestDeleteMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockDelete("monitor/services/dbaas/alert-definitions/123", nil) + + err := base.Client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID) + assert.NoError(t, err) +} From 639b89b3805168636f2544bdc39ccb7de5b243de Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Mon, 15 Sep 2025 19:07:03 +0530 Subject: [PATCH 11/24] DI-27070 Added more stingent integration tests --- monitor_alert_definitions.go | 5 + .../TestMonitorAlertDefinition_instance.yaml | 173 ++++++++++++++++++ .../monitor_alert_definitions_test.go | 123 +++++++++---- 3 files changed, 261 insertions(+), 40 deletions(-) create mode 100644 test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index 54ec35101..6ecebb2b9 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -18,6 +18,7 @@ type MonitorAlertDefinition struct { Status string `json:"status"` HasMoreResources bool `json:"has_more_resources"` Rule *Rule `json:"rule"` + RuleCriteria *RuleCriteria `json:"rule_criteria"` TriggerConditions *TriggerConditions `json:"trigger_conditions"` AlertChannels []AlertChannelEnvelope `json:"alert_channels"` Created *time.Time `json:"-"` @@ -67,6 +68,10 @@ const ( MonitorAlertDefinitionSeverityMinor = 3 ) +const ( + MonitorAlertDefinitionStatusEnabled = "enabled" + MonitorAlertDefinitionStatusDisabled = "disabled" +) // AlertChannelEnvelope represents a single alert channel entry returned inside alert definition type AlertChannelEnvelope struct { ID int `json:"id"` diff --git a/test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml b/test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml new file mode 100644 index 000000000..bc8824789 --- /dev/null +++ b/test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml @@ -0,0 +1,173 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/monitor/alert-definitions?page=1 + method: GET + response: + body: | + { + "data": [ + { + "id": 10000, + "label": "High Memory Usage Plan Dedicated", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:45:19", + "updated_by": "system", + "created_by": "system", + "entity_ids": [], + "description": "Alert triggers when dedicated plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "dedicated" + }, + { + "id": 10001, + "label": "High Memory Usage Plan Shared", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12", + "updated_by": "system", + "created_by": "system", + "entity_ids": [189690, 313392, 324828, 340839], + "description": "Alert triggers when shared plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "shared" + } + ], + "page": 1, + "pages": 1, + "results": 2 + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10001 + method: GET + response: + body: | + { + "id": 10001, + "label": "High Memory Usage Plan Shared", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12", + "updated_by": "system", + "created_by": "system", + "entity_ids": [189690, 313392, 324828, 340839], + "description": "Alert triggers when shared plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "shared" + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/monitor/alert-channels/ + method: GET + response: + body: | + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "details": { + "to": "ops@example.com" + }, + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12" + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" \ No newline at end of file diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index d7183274f..625ba162a 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -2,7 +2,6 @@ package integration import ( "context" - "fmt" "testing" "time" @@ -15,57 +14,62 @@ const ( ) func TestMonitorAlertDefinition_smoke(t *testing.T) { - client, teardown := createTestClient(t, "fixtures/TestMonitorAlertDefinition_smoke") + client, teardown := createTestClient(t, "fixtures/TestMonitorAlertDefinition_instance") defer teardown() - // instance, _, teardownInstance, err := setupInstance(t, "fixtures/TestMonitorAlertDefinition_instance", false) - // if err != nil { - // t.Fatalf("failed to setup instance: %s", err) - // } - // defer teardownInstance() - - // channel, teardownChannel, err := setupMonitorChannel(t, "fixtures/TestMonitorAlertDefinition_smoke_channel") - // if err != nil { - // t.Fatalf("failed to setup monitor channel: %s", err) - // } - // defer teardownChannel() - + //Get All Alert Definitions alerts, err := client.ListMonitorAlertDefinitions(context.Background(), "", nil) - fmt.Printf("Number of alerts: %d\n", len(alerts)) + + //Even if there is no alert definition, it should not error out if err != nil { t.Fatalf("failed to fetch monitor alert definitions: %s", err) } - for i, alert := range alerts { - fmt.Printf("Alert #%d: %+v\n", i+1, alert) + + // New: Iterate and log each alert definition for visibility + for _, alert := range alerts { + // Check few mandatory fields on each listed alert + assert.NotZero(t, alert.ID, "alert.ID should not be zero") + assert.NotEmpty(t, alert.Label, "alert.Label should not be empty") + + // If alert has a rule, validate basic rule structure + if alert.RuleCriteria != nil { + assert.NotEmpty(t, alert.RuleCriteria.Rules, "RuleCriteria.Rules should not be empty when RuleCriteria is provided") + for _, r := range alert.RuleCriteria.Rules { + assert.NotEmpty(t, r.Metric, "rule.Metric should not be empty") + assert.NotEmpty(t, r.Operator, "rule.Operator should not be empty") + } + } } + // Basic assertions based on the fixture assert.NoError(t, err) // Determine a channel ID to use for creating a new alert definition: var channelID int + var fetchedChannelLabel string + var fetchedChannelID int if len(alerts) > 0 && len(alerts[0].AlertChannels) > 0 { channelID = alerts[0].AlertChannels[0].ID + fetchedChannelID = alerts[0].AlertChannels[0].ID + fetchedChannelLabel = alerts[0].AlertChannels[0].Label } else { // Fallback to GetAlertChannels (some fixtures expose a single alert-channel endpoint) - fetchedChannel, err := client.GetAlertChannels(context.Background()) + fetchedChannelResp, err := client.GetAlertChannels(context.Background()) if err != nil { t.Fatalf("failed to determine a monitor channel to use: %s", err) } - channelID = fetchedChannel.ID + channelID = fetchedChannelResp.ID + fetchedChannelID = fetchedChannelResp.ID + fetchedChannelLabel = fetchedChannelResp.Label } - - service_type := "dbaas" - alertid := 10001 - fetchedAlert1, err := client.GetMonitorAlertDefinition(context.Background(), service_type, alertid) - if err != nil { - t.Fatalf("failed to fetch monitor alert definition: %s", err) - } - fmt.Printf("fetchedAlert: %+v\n", fetchedAlert1) + // Validate the chosen channel + assert.NotZero(t, fetchedChannelID, "fetchedChannel.ID should not be zero") + assert.NotEmpty(t, fetchedChannelLabel, "fetchedChannel.Label should not be empty") // Test creating a new Monitor Alert Definition createOpts := linodego.MonitorAlertDefinitionCreateOptions{ - Label: "go-test-alert-definition-creat1", - Severity: linodego.MonitorAlertDefinitionSeverityCritical, + Label: "go-test-alert-definition-create", + Severity: 3, Type: "user", Class: "test_class", Description: "Test alert definition creation", @@ -85,8 +89,8 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { Label: "Memory Usage", Metric: "memory_usage", Operator: "gt", - Threshold: floatPtr(90.0), - Unit: strPtr("percent"), + Threshold: func(f float64) *float64 { return &f }(90.0), + Unit: func(s string) *string { return &s }("percent"), DimensionFilters: []linodego.DimensionFilter{ { DimensionLabel: "node_type", @@ -116,19 +120,58 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { assert.ElementsMatch(t, createOpts.EntityIDs, createdAlert.EntityIDs) // assert.Equal(t, fetchedChannel.Label, createdAlert.AlertChannels[0].Label) + // More thorough assertions on the created alert's nested fields + if createdAlert.TriggerConditions != nil && createOpts.TriggerConditions != nil { + assert.Equal(t, createOpts.TriggerConditions.CriteriaCondition, createdAlert.TriggerConditions.CriteriaCondition) + assert.Equal(t, createOpts.TriggerConditions.EvaluationPeriodSeconds, createdAlert.TriggerConditions.EvaluationPeriodSeconds) + assert.Equal(t, createOpts.TriggerConditions.PollingIntervalSeconds, createdAlert.TriggerConditions.PollingIntervalSeconds) + assert.Equal(t, createOpts.TriggerConditions.TriggerOccurrences, createdAlert.TriggerConditions.TriggerOccurrences) + } + if createdAlert.RuleCriteria != nil && createOpts.RuleCriteria != nil { + assert.Equal(t, len(createOpts.RuleCriteria.Rules), len(createdAlert.RuleCriteria.Rules), "created alert should have same number of rules") + for i, r := range createOpts.RuleCriteria.Rules { + cr := createdAlert.RuleCriteria.Rules[i] + assert.Equal(t, r.Metric, cr.Metric) + assert.Equal(t, r.Operator, cr.Operator) + if r.Threshold != nil { + assert.NotNil(t, cr.Threshold) + assert.Equal(t, *r.Threshold, *cr.Threshold) + } + // Dimension filters + if len(r.DimensionFilters) > 0 { + assert.Equal(t, len(r.DimensionFilters), len(cr.DimensionFilters)) + for j, df := range r.DimensionFilters { + cdf := cr.DimensionFilters[j] + assert.Equal(t, df.DimensionLabel, cdf.DimensionLabel) + assert.Equal(t, df.Operator, cdf.Operator) + assert.Equal(t, df.Value, cdf.Value) + } + } + } + } + + // Update the created alert definition: change label only + newLabel := createdAlert.Label + "-updated" + updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ + Label: newLabel, + } + // wait for 1 minute before update for create to complete + time.Sleep(1 * time.Minute) + updatedAlert, err := client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID, updateOpts) + if err != nil { + // Some fixtures may not support update; treat as non-fatal + t.Logf("UpdateMonitorAlertDefinition returned error, skipping update assertions: %s", err) + } else { + assert.NotNil(t, updatedAlert) + assert.Equal(t, createdAlert.ID, updatedAlert.ID, "updated alert should keep same ID") + assert.Equal(t, newLabel, updatedAlert.Label, "updated alert should have the new label") + } + // Clean up created alert definition if createdAlert != nil { - // Wait for 2 minutes before deletion + // Wait for 2 minutes before deletion for update to complete time.Sleep(2 * time.Minute) err = client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID) assert.NoError(t, err) } } - -func floatPtr(f float64) *float64 { - return &f -} - -func strPtr(s string) *string { - return &s -} From b9157bb7df961882b95aa24e283a9b4beda02170 Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Mon, 15 Sep 2025 19:24:29 +0530 Subject: [PATCH 12/24] DI-27070 Added more stingent integration tests --- test/unit/monitor_alert_definitions_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/unit/monitor_alert_definitions_test.go b/test/unit/monitor_alert_definitions_test.go index 547df6e26..ba6058028 100644 --- a/test/unit/monitor_alert_definitions_test.go +++ b/test/unit/monitor_alert_definitions_test.go @@ -53,6 +53,8 @@ const ( "channel_ids": [1, 2], "is_enabled": false }` + + monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine = `{"id": 123, "label": "test-alert-definition-renamed-one-line", "severity": 1, "type": "some_type", "service_type": "dbaas", "status": "enabled", "entity_ids": ["12345"], "channel_ids": [1], "is_enabled": true}` ) func TestCreateMonitorAlertDefinition(t *testing.T) { @@ -150,6 +152,25 @@ func TestUpdateMonitorAlertDefinition(t *testing.T) { assert.Equal(t, 2, alert.Severity) } +func TestUpdateMonitorAlertDefinition_LabelOnly(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + // Mock a PUT that returns the single-line fixture + base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine)) + + updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ + Label: "test-alert-definition-renamed-one-line", + } + + alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition-renamed-one-line", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + func TestDeleteMonitorAlertDefinition(t *testing.T) { var base ClientBaseCase base.SetUp(t) From 05d97799cc39a69820560c0b22889b1201e3ed06 Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Mon, 15 Sep 2025 19:54:30 +0530 Subject: [PATCH 13/24] DI-27070 Fixed copilot suggestion for names --- monitor_alert_definitions.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index 6ecebb2b9..07397bbb9 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -30,7 +30,7 @@ type MonitorAlertDefinition struct { Class string `json:"class"` } -// AlertTriggerConditions represents the trigger conditions for an alert. +// TriggerConditions represents the trigger conditions for an alert. type TriggerConditions struct { CriteriaCondition string `json:"criteria_condition,omitempty"` EvaluationPeriodSeconds int `json:"evaluation_period_seconds,omitempty"` @@ -38,7 +38,7 @@ type TriggerConditions struct { TriggerOccurrences int `json:"trigger_occurrences,omitempty"` } -// AlertRuleCriteria represents the rule criteria for an alert. +// RuleCriteria represents the rule criteria for an alert. type RuleCriteria struct { Rules []Rule `json:"rules,omitempty"` } @@ -62,16 +62,11 @@ type DimensionFilter struct { Value interface{} `json:"value"` } -const ( - MonitorAlertDefinitionSeverityCritical = 1 - MonitorAlertDefinitionSeverityMajor = 2 - MonitorAlertDefinitionSeverityMinor = 3 -) - const ( MonitorAlertDefinitionStatusEnabled = "enabled" MonitorAlertDefinitionStatusDisabled = "disabled" ) + // AlertChannelEnvelope represents a single alert channel entry returned inside alert definition type AlertChannelEnvelope struct { ID int `json:"id"` From d90a6c673f6c0b1b9e3d57db834f082f1fc030a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:10:57 -0400 Subject: [PATCH 14/24] build(deps): bump golang.org/x/oauth2 from 0.30.0 to 0.31.0 (#812) * build(deps): bump golang.org/x/oauth2 from 0.30.0 to 0.31.0 Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.30.0 to 0.31.0. - [Commits](https://github.com/golang/oauth2/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-version: 0.31.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * make tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhiwei Liang --- go.mod | 2 +- go.sum | 4 ++-- k8s/go.mod | 2 +- k8s/go.sum | 4 ++-- test/go.mod | 2 +- test/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7d94a5fde..3d59b274d 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/go-querystring v1.1.0 github.com/jarcoal/httpmock v1.4.1 golang.org/x/net v0.44.0 - golang.org/x/oauth2 v0.30.0 + golang.org/x/oauth2 v0.31.0 golang.org/x/text v0.29.0 gopkg.in/ini.v1 v1.66.6 ) diff --git a/go.sum b/go.sum index bbe49e194..9c0b66587 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= diff --git a/k8s/go.mod b/k8s/go.mod index 2081bf1e3..f52560b52 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -30,7 +30,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.44.0 // indirect - golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/oauth2 v0.31.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect diff --git a/k8s/go.sum b/k8s/go.sum index 421eca7ed..1a3e31e29 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -93,8 +93,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/test/go.mod b/test/go.mod index 39896d72a..1d180d956 100644 --- a/test/go.mod +++ b/test/go.mod @@ -9,7 +9,7 @@ require ( github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 golang.org/x/net v0.44.0 - golang.org/x/oauth2 v0.30.0 + golang.org/x/oauth2 v0.31.0 k8s.io/client-go v0.29.4 ) diff --git a/test/go.sum b/test/go.sum index 8c3aaad21..50464214c 100644 --- a/test/go.sum +++ b/test/go.sum @@ -102,8 +102,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 57b2014cfbae1aca1799115a211c6e83927ab311 Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Tue, 16 Sep 2025 08:32:17 +0530 Subject: [PATCH 15/24] DI-27070 made it consistent with pythonSDK naming conventions --- monitor_alert_definitions.go | 99 +++++++++++-------- .../monitor_alert_definitions_test.go | 8 +- test/unit/monitor_alert_definitions_test.go | 23 ++--- 3 files changed, 69 insertions(+), 61 deletions(-) diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index 07397bbb9..bfbfc0a6f 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -8,8 +8,8 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// MonitorAlertDefinition represents an ACLP Alert Definition object -type MonitorAlertDefinition struct { +// AlertDefinition represents an ACLP Alert Definition object +type AlertDefinition struct { ID int `json:"id"` Label string `json:"label"` Severity int `json:"severity"` @@ -30,6 +30,9 @@ type MonitorAlertDefinition struct { Class string `json:"class"` } +// Backwards-compatible alias +type MonitorAlertDefinition = AlertDefinition + // TriggerConditions represents the trigger conditions for an alert. type TriggerConditions struct { CriteriaCondition string `json:"criteria_condition,omitempty"` @@ -62,11 +65,6 @@ type DimensionFilter struct { Value interface{} `json:"value"` } -const ( - MonitorAlertDefinitionStatusEnabled = "enabled" - MonitorAlertDefinitionStatusDisabled = "disabled" -) - // AlertChannelEnvelope represents a single alert channel entry returned inside alert definition type AlertChannelEnvelope struct { ID int `json:"id"` @@ -75,38 +73,53 @@ type AlertChannelEnvelope struct { URL string `json:"url"` } -// MonitorAlertDefinitionCreateOptions are the options used to create a new alert definition. -type MonitorAlertDefinitionCreateOptions struct { - Label string `json:"label"` - Severity int `json:"severity"` - Class string `json:"class"` - Type string `json:"type"` - Description string `json:"description,omitempty"` - ChannelIDs []int `json:"channel_ids"` - EntityIDs []string `json:"entity_ids"` - IsEnabled bool `json:"is_enabled"` - TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` - Rule *Rule `json:"rule,omitempty"` - RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` +// AlertType represents the type of alert: "user" or "system" +type AlertType string + +const ( + AlertTypeUser AlertType = "user" + AlertTypeSystem AlertType = "system" +) + +// Severity represents the severity level of an alert. +// 0 = Severe, 1 = Medium, 2 = Low, 3 = Info +type Severity int + +const ( + SeveritySevere Severity = 0 + SeverityMedium Severity = 1 + SeverityLow Severity = 2 + SeverityInfo Severity = 3 +) + +// AlertDefinitionCreateOptions are the options used to create a new alert definition. +type AlertDefinitionCreateOptions struct { + ServiceType string `json:"service_type"` // mandatory + Label string `json:"label"` // mandatory + Severity int `json:"severity"` // mandatory + ChannelIDs []int `json:"channel_ids"` // mandatory + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` // optional + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` // optional + EntityIDs []string `json:"entity_ids,omitempty"` // optional + Description string `json:"description,omitempty"` // optional } -// MonitorAlertDefinitionUpdateOptions are the options used to update an alert definition. -type MonitorAlertDefinitionUpdateOptions struct { - Label string `json:"label,omitempty"` - Severity int `json:"severity,omitempty"` - Class string `json:"class,omitempty"` - Description string `json:"description,omitempty"` - ChannelIDs []int `json:"channel_ids,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` - IsEnabled *bool `json:"is_enabled,omitempty"` - TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` - Rule *Rule `json:"rule,omitempty"` - RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` +// AlertDefinitionUpdateOptions are the options used to update an alert definition. +type AlertDefinitionUpdateOptions struct { + ServiceType string `json:"service_type"` // mandatory, must not be empty + AlertID int `json:"alert_id"` // mandatory, must not be zero + Label *string `json:"label,omitempty"` // optional + Severity *int `json:"severity,omitempty"` // optional, should be int to match AlertDefinition + Description *string `json:"description,omitempty"` // optional + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` // optional + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` // optional + EntityIDs []string `json:"entity_ids,omitempty"` // optional + ChannelIDs []int `json:"channel_ids,omitempty"` // optional } // UnmarshalJSON implements the json.Unmarshaler interface -func (i *MonitorAlertDefinition) UnmarshalJSON(b []byte) error { - type Mask MonitorAlertDefinition +func (i *AlertDefinition) UnmarshalJSON(b []byte) error { + type Mask AlertDefinition p := struct { *Mask @@ -134,27 +147,27 @@ func (c *Client) ListMonitorAlertDefinitions(ctx context.Context, serviceType st } else { endpoint = formatAPIV4BetaPath("monitor/alert-definitions") } - return getPaginatedResults[MonitorAlertDefinition](ctx, c, endpoint, opts) + return getPaginatedResults[AlertDefinition](ctx, c, endpoint, opts) } // GetMonitorAlertDefinition gets an ACLP Monitor Alert Definition. func (c *Client) GetMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int) (*MonitorAlertDefinition, error) { e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) - return doGETRequest[MonitorAlertDefinition](ctx, c, e) + return doGETRequest[AlertDefinition](ctx, c, e) } // CreateMonitorAlertDefinition creates an ACLP Monitor Alert Definition. -func (c *Client) CreateMonitorAlertDefinition(ctx context.Context, serviceType string, opts MonitorAlertDefinitionCreateOptions) (*MonitorAlertDefinition, error) { +func (c *Client) CreateMonitorAlertDefinition(ctx context.Context, serviceType string, opts AlertDefinitionCreateOptions) (*MonitorAlertDefinition, error) { e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) - return doPOSTRequest[MonitorAlertDefinition](ctx, c, e, opts) + return doPOSTRequest[AlertDefinition](ctx, c, e, opts) } // CreateMonitorAlertDefinitionWithIdempotency creates an ACLP Monitor Alert Definition // and optionally sends an Idempotency-Key header to make the request idempotent. -func (c *Client) CreateMonitorAlertDefinitionWithIdempotency(ctx context.Context, serviceType string, opts MonitorAlertDefinitionCreateOptions, idempotencyKey string) (*MonitorAlertDefinition, error) { +func (c *Client) CreateMonitorAlertDefinitionWithIdempotency(ctx context.Context, serviceType string, opts AlertDefinitionCreateOptions, idempotencyKey string) (*MonitorAlertDefinition, error) { e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) - var result MonitorAlertDefinition + var result AlertDefinition req := c.R(ctx).SetResult(&result) if idempotencyKey != "" { @@ -173,13 +186,13 @@ func (c *Client) CreateMonitorAlertDefinitionWithIdempotency(ctx context.Context return nil, err } - return r.Result().(*MonitorAlertDefinition), nil + return r.Result().(*AlertDefinition), nil } // UpdateMonitorAlertDefinition updates an ACLP Monitor Alert Definition. -func (c *Client) UpdateMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int, opts MonitorAlertDefinitionUpdateOptions) (*MonitorAlertDefinition, error) { +func (c *Client) UpdateMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int, opts AlertDefinitionUpdateOptions) (*AlertDefinition, error) { e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) - return doPUTRequest[MonitorAlertDefinition, MonitorAlertDefinitionUpdateOptions](ctx, c, e, opts) + return doPUTRequest[AlertDefinition](ctx, c, e, opts) } // DeleteMonitorAlertDefinition deletes an ACLP Monitor Alert Definition. diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index 625ba162a..1955f2b92 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -67,10 +67,10 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { assert.NotEmpty(t, fetchedChannelLabel, "fetchedChannel.Label should not be empty") // Test creating a new Monitor Alert Definition - createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + createOpts := linodego.AlertDefinitionCreateOptions{ Label: "go-test-alert-definition-create", - Severity: 3, - Type: "user", + Severity: int(linodego.SeverityLow), + Type: string(linodego.AlertTypeUser), Class: "test_class", Description: "Test alert definition creation", ChannelIDs: []int{channelID}, @@ -152,7 +152,7 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { // Update the created alert definition: change label only newLabel := createdAlert.Label + "-updated" - updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ + updateOpts := linodego.AlertDefinitionUpdateOptions{ Label: newLabel, } // wait for 1 minute before update for create to complete diff --git a/test/unit/monitor_alert_definitions_test.go b/test/unit/monitor_alert_definitions_test.go index ba6058028..48fc159b8 100644 --- a/test/unit/monitor_alert_definitions_test.go +++ b/test/unit/monitor_alert_definitions_test.go @@ -64,13 +64,11 @@ func TestCreateMonitorAlertDefinition(t *testing.T) { base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) - createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + createOpts := linodego.AlertDefinitionCreateOptions{ Label: "test-alert-definition", - Severity: 1, - Type: "some_type", + Severity: int(linodego.SeverityLow), ChannelIDs: []int{1}, EntityIDs: []string{"12345"}, - IsEnabled: true, } alert, err := base.Client.CreateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createOpts) @@ -87,13 +85,11 @@ func TestCreateMonitorAlertDefinitionWithIdempotency(t *testing.T) { base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) - createOpts := linodego.MonitorAlertDefinitionCreateOptions{ + createOpts := linodego.AlertDefinitionCreateOptions{ Label: "test-alert-definition", - Severity: 1, - Type: "some_type", + Severity: int(linodego.SeverityLow), ChannelIDs: []int{1}, EntityIDs: []string{"12345"}, - IsEnabled: true, } alert, err := base.Client.CreateMonitorAlertDefinitionWithIdempotency(context.Background(), testMonitorAlertDefinitionServiceType, createOpts, "idempotency-key") @@ -138,10 +134,9 @@ func TestUpdateMonitorAlertDefinition(t *testing.T) { base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateResponse)) - updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ - Label: "test-alert-definition-renamed", - Severity: 2, - IsEnabled: linodego.Pointer(false), + updateOpts := linodego.AlertDefinitionUpdateOptions{ + Label: linodego.Pointer("test-alert-definition-renamed"), + Severity: linodego.Pointer("low"), ChannelIDs: []int{1, 2}, } @@ -160,8 +155,8 @@ func TestUpdateMonitorAlertDefinition_LabelOnly(t *testing.T) { // Mock a PUT that returns the single-line fixture base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine)) - updateOpts := linodego.MonitorAlertDefinitionUpdateOptions{ - Label: "test-alert-definition-renamed-one-line", + updateOpts := linodego.AlertDefinitionUpdateOptions{ + Label: linodego.Pointer("test-alert-definition-renamed-one-line"), } alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) From bc5bc7e74e0e76fae3cd69e299985fb8503f5720 Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Tue, 16 Sep 2025 09:17:45 +0530 Subject: [PATCH 16/24] DI-27070 Fixed Datatype issues --- monitor_alert_definitions.go | 6 +++--- test/integration/monitor_alert_definitions_test.go | 1 - test/unit/monitor_alert_definitions_test.go | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index bfbfc0a6f..cf8fd2b6e 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -108,9 +108,9 @@ type AlertDefinitionCreateOptions struct { type AlertDefinitionUpdateOptions struct { ServiceType string `json:"service_type"` // mandatory, must not be empty AlertID int `json:"alert_id"` // mandatory, must not be zero - Label *string `json:"label,omitempty"` // optional - Severity *int `json:"severity,omitempty"` // optional, should be int to match AlertDefinition - Description *string `json:"description,omitempty"` // optional + Label string `json:"label,omitempty"` // optional + Severity int `json:"severity,omitempty"` // optional, should be int to match AlertDefinition + Description string `json:"description,omitempty"` // optional RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` // optional TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` // optional EntityIDs []string `json:"entity_ids,omitempty"` // optional diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index 1955f2b92..8a332cb64 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -75,7 +75,6 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { Description: "Test alert definition creation", ChannelIDs: []int{channelID}, EntityIDs: nil, - IsEnabled: true, TriggerConditions: &linodego.TriggerConditions{ CriteriaCondition: "ALL", EvaluationPeriodSeconds: 300, diff --git a/test/unit/monitor_alert_definitions_test.go b/test/unit/monitor_alert_definitions_test.go index 48fc159b8..dcfab3c30 100644 --- a/test/unit/monitor_alert_definitions_test.go +++ b/test/unit/monitor_alert_definitions_test.go @@ -135,8 +135,8 @@ func TestUpdateMonitorAlertDefinition(t *testing.T) { base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateResponse)) updateOpts := linodego.AlertDefinitionUpdateOptions{ - Label: linodego.Pointer("test-alert-definition-renamed"), - Severity: linodego.Pointer("low"), + Label: "test-alert-definition-renamed", + Severity: int(linodego.SeverityLow), ChannelIDs: []int{1, 2}, } @@ -156,7 +156,7 @@ func TestUpdateMonitorAlertDefinition_LabelOnly(t *testing.T) { base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine)) updateOpts := linodego.AlertDefinitionUpdateOptions{ - Label: linodego.Pointer("test-alert-definition-renamed-one-line"), + Label: "test-alert-definition-renamed-one-line", } alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) From c3de9a154566096852b7b252a17c6ad3464f9ad2 Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Tue, 16 Sep 2025 09:54:35 +0530 Subject: [PATCH 17/24] DI-27070 renamed files and corrected format --- alert_channels.go | 79 ++++++++++++++++ monitor_alert_definitions.go | 8 -- monitor_channels.go | 91 ------------------- .../monitor_alert_definitions_test.go | 3 - 4 files changed, 79 insertions(+), 102 deletions(-) create mode 100644 alert_channels.go delete mode 100644 monitor_channels.go diff --git a/alert_channels.go b/alert_channels.go new file mode 100644 index 000000000..d609fd178 --- /dev/null +++ b/alert_channels.go @@ -0,0 +1,79 @@ +package linodego + +import ( + "context" +) + +// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition +type AlertChannelEnvelope struct { + ID int `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + URL string `json:"url"` +} + +// AlertChannel represents a Monitor Channel object. +type AlertChannel struct { + ID int `json:"id"` + Alerts []AlertChannelEnvelope `json:"alerts"` + Label string `json:"label"` + Channel_type string `json:"channel_type"` + Content ChannelContent `json:"content"` + Type AlertType `json:"type"` + Details AlertChannelDetail `json:"details"` + Created string `json:"created"` + Created_by string `json:"created_by"` + Updated string `json:"updated"` + Updated_by string `json:"updated_by"` +} + +// AlertChannelDetail represents the details of a Monitor Channel. +type AlertChannelDetail struct { + To string `json:"to,omitempty"` + From string `json:"from,omitempty"` + User string `json:"user,omitempty"` + Token string `json:"token,omitempty"` + URL string `json:"url,omitempty"` +} + +// AlertChannelCreateOptions are the options used to create a new Monitor Channel. +type AlertChannelCreateOptions struct { + Label string `json:"label"` + Type string `json:"type"` + Details AlertChannelDetailOptions `json:"details"` +} + +// AlertChannelDetailOptions are the options used to create the details of a new Monitor Channel. +type AlertChannelDetailOptions struct { + To string `json:"to,omitempty"` +} + +// Backwards-compat alias for older name +type AlertingChannelCreateOptions = AlertChannelCreateOptions + +type EmailChannelContent struct { + EmailAddresses []string `json:"email_addresses"` +} + +// ChannelContent represents the content block for an AlertChannel, which varies by channel type. +type ChannelContent struct { + Email *EmailChannelContent `json:"email,omitempty"` + // Other channel types like 'webhook', 'slack' could be added here as optional fields. +} + +// ListMonitorChannels gets a paginated list of Monitor Channels. +func (c *Client) ListAlertChannels(ctx context.Context, opts *ListOptions) ([]AlertChannel, error) { + endpoint := formatAPIV4BetaPath("monitor/channels") + return getPaginatedResults[AlertChannel](ctx, c, endpoint, opts) +} + +// GetMonitorChannel gets a Monitor Channel by ID. +func (c *Client) GetAlertChannel(ctx context.Context, channelID int) (*AlertChannel, error) { + e := formatAPIV4BetaPath("monitor/channels/%d", channelID) + return doGETRequest[AlertChannel](ctx, c, e) +} + +func (c *Client) GetAlertChannels(ctx context.Context) (*AlertChannel, error) { + e := formatAPIV4BetaPath("monitor/alert-channels/") + return doGETRequest[AlertChannel](ctx, c, e) +} diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index cf8fd2b6e..ba842d7a5 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -65,14 +65,6 @@ type DimensionFilter struct { Value interface{} `json:"value"` } -// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition -type AlertChannelEnvelope struct { - ID int `json:"id"` - Label string `json:"label"` - Type string `json:"type"` - URL string `json:"url"` -} - // AlertType represents the type of alert: "user" or "system" type AlertType string diff --git a/monitor_channels.go b/monitor_channels.go deleted file mode 100644 index a56601ea2..000000000 --- a/monitor_channels.go +++ /dev/null @@ -1,91 +0,0 @@ -package linodego - -import ( - "context" - "encoding/json" - "time" - - "github.com/linode/linodego/internal/parseabletime" -) - -// MonitorChannel represents a Monitor Channel object. -type MonitorChannel struct { - ID int `json:"id"` - Label string `json:"label"` - Type string `json:"type"` - Details MonitorChannelDetail `json:"details"` - Created *time.Time `json:"-"` - Updated *time.Time `json:"-"` -} - -// MonitorChannelDetail represents the details of a Monitor Channel. -type MonitorChannelDetail struct { - To string `json:"to,omitempty"` - From string `json:"from,omitempty"` - User string `json:"user,omitempty"` - Token string `json:"token,omitempty"` - URL string `json:"url,omitempty"` -} - -// MonitorChannelCreateOptions are the options used to create a new Monitor Channel. -type MonitorChannelCreateOptions struct { - Label string `json:"label"` - Type string `json:"type"` - Details MonitorChannelDetailOptions `json:"details"` -} - -// MonitorChannelDetailOptions are the options used to create the details of a new Monitor Channel. -type MonitorChannelDetailOptions struct { - To string `json:"to,omitempty"` -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (i *MonitorChannel) UnmarshalJSON(b []byte) error { - type Mask MonitorChannel - - p := struct { - *Mask - Created *parseabletime.ParseableTime `json:"created"` - Updated *parseabletime.ParseableTime `json:"updated"` - }{ - Mask: (*Mask)(i), - } - - if err := json.Unmarshal(b, &p); err != nil { - return err - } - - i.Created = (*time.Time)(p.Created) - i.Updated = (*time.Time)(p.Updated) - - return nil -} - -// CreateMonitorChannel creates a new Monitor Channel. -func (c *Client) CreateMonitorChannel(ctx context.Context, opts MonitorChannelCreateOptions) (*MonitorChannel, error) { - e := "v4beta/monitor/channels" - return doPOSTRequest[MonitorChannel, MonitorChannelCreateOptions](ctx, c, e, opts) -} - -// DeleteMonitorChannel deletes a Monitor Channel. -func (c *Client) DeleteMonitorChannel(ctx context.Context, channelID int) error { - e := formatAPIV4BetaPath("monitor/channels/%d", channelID) - return doDELETERequest(ctx, c, e) -} - -// ListMonitorChannels gets a paginated list of Monitor Channels. -func (c *Client) ListMonitorChannels(ctx context.Context, opts *ListOptions) ([]MonitorChannel, error) { - endpoint := formatAPIV4BetaPath("monitor/channels") - return getPaginatedResults[MonitorChannel](ctx, c, endpoint, opts) -} - -// GetMonitorChannel gets a Monitor Channel by ID. -func (c *Client) GetMonitorChannel(ctx context.Context, channelID int) (*MonitorChannel, error) { - e := formatAPIV4BetaPath("monitor/channels/%d", channelID) - return doGETRequest[MonitorChannel](ctx, c, e) -} - -func (c *Client) GetAlertChannels(ctx context.Context) (*MonitorChannel, error) { - e := formatAPIV4BetaPath("monitor/alert-channels/") - return doGETRequest[MonitorChannel](ctx, c, e) -} diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index 8a332cb64..eef9684d8 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -70,8 +70,6 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { createOpts := linodego.AlertDefinitionCreateOptions{ Label: "go-test-alert-definition-create", Severity: int(linodego.SeverityLow), - Type: string(linodego.AlertTypeUser), - Class: "test_class", Description: "Test alert definition creation", ChannelIDs: []int{channelID}, EntityIDs: nil, @@ -114,7 +112,6 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { assert.NotNil(t, createdAlert) assert.Equal(t, createOpts.Label, createdAlert.Label) assert.Equal(t, createOpts.Severity, createdAlert.Severity) - assert.Equal(t, createOpts.Type, createdAlert.Type) assert.Equal(t, createOpts.Description, createdAlert.Description) assert.ElementsMatch(t, createOpts.EntityIDs, createdAlert.EntityIDs) // assert.Equal(t, fetchedChannel.Label, createdAlert.AlertChannels[0].Label) From 87495c19cdd3c86575e470525c76bd6de9335702 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 16 Sep 2025 09:54:22 -0400 Subject: [PATCH 18/24] change isPublic to be a pointer on new IPv6/linode interface config to be in line with other isPublic values (#826) --- instance_config_interfaces.go | 6 +++--- interfaces.go | 4 ++-- test/unit/instance_config_interfaces_test.go | 8 ++++---- test/unit/instance_config_test.go | 8 ++++---- test/unit/interface_test.go | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/instance_config_interfaces.go b/instance_config_interfaces.go index e7454618f..c1c8735f8 100644 --- a/instance_config_interfaces.go +++ b/instance_config_interfaces.go @@ -27,7 +27,7 @@ type InstanceConfigInterface struct { type InstanceConfigInterfaceIPv6 struct { SLAAC []InstanceConfigInterfaceIPv6SLAAC `json:"slaac"` Ranges []InstanceConfigInterfaceIPv6Range `json:"ranges"` - IsPublic bool `json:"is_public"` + IsPublic *bool `json:"is_public"` } // InstanceConfigInterfaceIPv6SLAAC represents a single IPv6 SLAAC under @@ -173,7 +173,7 @@ func (i InstanceConfigInterface) GetCreateOptions() InstanceConfigInterfaceCreat } }, ), - IsPublic: copyValue(&ipv6.IsPublic), + IsPublic: copyValue(ipv6.IsPublic), } } @@ -219,7 +219,7 @@ func (i InstanceConfigInterface) GetUpdateOptions() InstanceConfigInterfaceUpdat opts.IPv6 = &InstanceConfigInterfaceUpdateOptionsIPv6{ SLAAC: &newSLAAC, Ranges: &newRanges, - IsPublic: copyValue(&ipv6.IsPublic), + IsPublic: copyValue(ipv6.IsPublic), } } } diff --git a/interfaces.go b/interfaces.go index b10e6cdfe..6badfe6e6 100644 --- a/interfaces.go +++ b/interfaces.go @@ -90,7 +90,7 @@ type VPCInterfaceIPv4Range struct { type VPCInterfaceIPv6 struct { SLAAC []VPCInterfaceIPv6SLAAC `json:"slaac"` Ranges []VPCInterfaceIPv6Range `json:"ranges"` - IsPublic bool `json:"is_public"` + IsPublic *bool `json:"is_public"` } // VPCInterfaceIPv6SLAAC contains the information for a single IPv6 SLAAC under a VPC. @@ -174,7 +174,7 @@ type VPCInterfaceIPv4RangeCreateOptions struct { type VPCInterfaceIPv6CreateOptions struct { SLAAC []VPCInterfaceIPv6SLAACCreateOptions `json:"slaac,omitempty"` Ranges []VPCInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` - IsPublic bool `json:"is_public"` + IsPublic *bool `json:"is_public"` } // VPCInterfaceIPv6SLAACCreateOptions defines the IPv6 SLAAC configuration parameters for VPC creation. diff --git a/test/unit/instance_config_interfaces_test.go b/test/unit/instance_config_interfaces_test.go index 96857ad1a..c78f4e9a8 100644 --- a/test/unit/instance_config_interfaces_test.go +++ b/test/unit/instance_config_interfaces_test.go @@ -31,7 +31,7 @@ func TestInstanceConfigInterface_List(t *testing.T) { assert.Equal(t, "1234::5678/64", interfaces[0].IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", interfaces[0].IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", interfaces[0].IPv6.Ranges[0].Range) - assert.True(t, interfaces[0].IPv6.IsPublic) + assert.True(t, *interfaces[0].IPv6.IsPublic) } func TestInstanceConfigInterface_Get(t *testing.T) { @@ -56,7 +56,7 @@ func TestInstanceConfigInterface_Get(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Create(t *testing.T) { @@ -95,7 +95,7 @@ func TestInstanceConfigInterface_Create(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Update(t *testing.T) { @@ -130,7 +130,7 @@ func TestInstanceConfigInterface_Update(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Delete(t *testing.T) { diff --git a/test/unit/instance_config_test.go b/test/unit/instance_config_test.go index 3b0ee76ee..34473dac3 100644 --- a/test/unit/instance_config_test.go +++ b/test/unit/instance_config_test.go @@ -43,7 +43,7 @@ func TestInstanceConfig_List(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -85,7 +85,7 @@ func TestInstanceConfig_Get(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -134,7 +134,7 @@ func TestInstanceConfig_Create(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -180,7 +180,7 @@ func TestInstanceConfig_Update(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } diff --git a/test/unit/interface_test.go b/test/unit/interface_test.go index 115f9efdb..68ff785ec 100644 --- a/test/unit/interface_test.go +++ b/test/unit/interface_test.go @@ -138,7 +138,7 @@ func TestInterface_GetVPC(t *testing.T) { assert.Equal(t, "4321::/64", iface.VPC.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.VPC.IPv6.IsPublic) + assert.Equal(t, true, *iface.VPC.IPv6.IsPublic) } func TestInterface_CreatePublic(t *testing.T) { @@ -277,7 +277,7 @@ func TestInterface_UpdateVPC(t *testing.T) { assert.Equal(t, "4322::/64", iface.VPC.IPv6.Ranges[0].Range) - assert.Equal(t, false, iface.VPC.IPv6.IsPublic) + assert.Equal(t, false, *iface.VPC.IPv6.IsPublic) } func TestInterface_Upgrade(t *testing.T) { From 39b4246498389dc44f3b7e87f1f8ff00f316e6a1 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:06:57 -0400 Subject: [PATCH 19/24] Change types of interfaces create options slices to be pointers (#817) * Change types of interfaces create options slices to be pointers * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- interfaces.go | 14 +++++++------- test/integration/instance_interfaces_test.go | 4 ++-- test/unit/interface_test.go | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/interfaces.go b/interfaces.go index 6badfe6e6..bfc47793d 100644 --- a/interfaces.go +++ b/interfaces.go @@ -132,7 +132,7 @@ type PublicInterfaceCreateOptions struct { } type PublicInterfaceIPv4CreateOptions struct { - Addresses []PublicInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` + Addresses *[]PublicInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` } type PublicInterfaceIPv4AddressCreateOptions struct { @@ -141,7 +141,7 @@ type PublicInterfaceIPv4AddressCreateOptions struct { } type PublicInterfaceIPv6CreateOptions struct { - Ranges []PublicInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` + Ranges *[]PublicInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` } type PublicInterfaceIPv6RangeCreateOptions struct { @@ -155,8 +155,8 @@ type VPCInterfaceCreateOptions struct { } type VPCInterfaceIPv4CreateOptions struct { - Addresses []VPCInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` - Ranges []VPCInterfaceIPv4RangeCreateOptions `json:"ranges,omitempty"` + Addresses *[]VPCInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` + Ranges *[]VPCInterfaceIPv4RangeCreateOptions `json:"ranges,omitempty"` } type VPCInterfaceIPv4AddressCreateOptions struct { @@ -172,9 +172,9 @@ type VPCInterfaceIPv4RangeCreateOptions struct { // VPCInterfaceIPv6CreateOptions specifies IPv6 configuration parameters for VPC creation. // NOTE: IPv6 interfaces may not currently be available to all users. type VPCInterfaceIPv6CreateOptions struct { - SLAAC []VPCInterfaceIPv6SLAACCreateOptions `json:"slaac,omitempty"` - Ranges []VPCInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` - IsPublic *bool `json:"is_public"` + SLAAC *[]VPCInterfaceIPv6SLAACCreateOptions `json:"slaac,omitempty"` + Ranges *[]VPCInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` + IsPublic *bool `json:"is_public"` } // VPCInterfaceIPv6SLAACCreateOptions defines the IPv6 SLAAC configuration parameters for VPC creation. diff --git a/test/integration/instance_interfaces_test.go b/test/integration/instance_interfaces_test.go index 933f52094..56e20180c 100644 --- a/test/integration/instance_interfaces_test.go +++ b/test/integration/instance_interfaces_test.go @@ -102,7 +102,7 @@ func TestInstance_CreateWithLinodeInterfaces( FirewallID: linodego.DoublePointer(firewallID), Public: &linodego.PublicInterfaceCreateOptions{ IPv4: &linodego.PublicInterfaceIPv4CreateOptions{ - Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.PublicInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), @@ -117,7 +117,7 @@ func TestInstance_CreateWithLinodeInterfaces( VPC: &linodego.VPCInterfaceCreateOptions{ SubnetID: vpcSubnet.ID, IPv4: &linodego.VPCInterfaceIPv4CreateOptions{ - Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.VPCInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), diff --git a/test/unit/interface_test.go b/test/unit/interface_test.go index 68ff785ec..ede205328 100644 --- a/test/unit/interface_test.go +++ b/test/unit/interface_test.go @@ -158,7 +158,7 @@ func TestInterface_CreatePublic(t *testing.T) { opts := linodego.LinodeInterfaceCreateOptions{ Public: &linodego.PublicInterfaceCreateOptions{ IPv4: &linodego.PublicInterfaceIPv4CreateOptions{ - Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.PublicInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), @@ -227,13 +227,13 @@ func TestInterface_UpdateVPC(t *testing.T) { }, VPC: &linodego.VPCInterfaceCreateOptions{ IPv4: &linodego.VPCInterfaceIPv4CreateOptions{ - Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.VPCInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("192.168.23.4"), Primary: linodego.Pointer(true), }, }, - Ranges: []linodego.VPCInterfaceIPv4RangeCreateOptions{ + Ranges: &[]linodego.VPCInterfaceIPv4RangeCreateOptions{ { Range: "192.168.23.16/28", }, @@ -243,14 +243,14 @@ func TestInterface_UpdateVPC(t *testing.T) { }, }, IPv6: &linodego.VPCInterfaceIPv6CreateOptions{ - SLAAC: []linodego.VPCInterfaceIPv6SLAACCreateOptions{ + SLAAC: &[]linodego.VPCInterfaceIPv6SLAACCreateOptions{ { Range: "1235::/64", }, }, - Ranges: []linodego.VPCInterfaceIPv6RangeCreateOptions{ + Ranges: &[]linodego.VPCInterfaceIPv6RangeCreateOptions{ { - "4322::/64", + Range: "4322::/64", }, }, }, From 587e8d37ec8e3ef0d6a03642e15cec5bae330b5c Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:09:38 -0400 Subject: [PATCH 20/24] Add VPCInterfaceUpdateOptions (#825) --- interfaces.go | 8 ++++++-- test/unit/interface_test.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/interfaces.go b/interfaces.go index bfc47793d..d91d45e2a 100644 --- a/interfaces.go +++ b/interfaces.go @@ -122,8 +122,7 @@ type LinodeInterfaceCreateOptions struct { type LinodeInterfaceUpdateOptions struct { DefaultRoute *InterfaceDefaultRoute `json:"default_route,omitempty"` Public *PublicInterfaceCreateOptions `json:"public,omitempty"` - VPC *VPCInterfaceCreateOptions `json:"vpc,omitempty"` - VLAN *VLANInterface `json:"vlan,omitempty"` + VPC *VPCInterfaceUpdateOptions `json:"vpc,omitempty"` } type PublicInterfaceCreateOptions struct { @@ -189,6 +188,11 @@ type VPCInterfaceIPv6RangeCreateOptions struct { Range string `json:"range"` } +type VPCInterfaceUpdateOptions struct { + IPv4 *VPCInterfaceIPv4CreateOptions `json:"ipv4,omitempty"` + IPv6 *VPCInterfaceIPv6CreateOptions `json:"ipv6,omitempty"` +} + type LinodeInterfacesUpgrade struct { ConfigID int `json:"config_id"` DryRun bool `json:"dry_run"` diff --git a/test/unit/interface_test.go b/test/unit/interface_test.go index ede205328..fb5e5d2d3 100644 --- a/test/unit/interface_test.go +++ b/test/unit/interface_test.go @@ -225,7 +225,7 @@ func TestInterface_UpdateVPC(t *testing.T) { IPv4: linodego.Pointer(true), IPv6: linodego.Pointer(true), }, - VPC: &linodego.VPCInterfaceCreateOptions{ + VPC: &linodego.VPCInterfaceUpdateOptions{ IPv4: &linodego.VPCInterfaceIPv4CreateOptions{ Addresses: &[]linodego.VPCInterfaceIPv4AddressCreateOptions{ { From d379928a63690848b85c95e4fc6e6d5b04adfd33 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:50:47 -0400 Subject: [PATCH 21/24] Format everything with `golangci-lint` (#830) * Run golangci-lint fmt * Fix go docs --- account.go | 2 +- account_agreements.go | 2 +- account_betas.go | 4 +++- account_maintenance.go | 2 +- account_oauth_client.go | 4 ++-- account_payments.go | 2 +- account_users.go | 4 ++-- betas.go | 2 +- client.go | 4 ++-- client_monitor.go | 2 +- domain_records.go | 2 +- domains.go | 2 +- firewall_devices.go | 2 +- firewall_templates.go | 4 ++-- images.go | 2 +- instance_ips.go | 2 +- lke_clusters.go | 4 ++-- lke_node_pools.go | 4 ++-- longview.go | 2 +- monitor_dashboards.go | 8 ++++---- monitor_metrics_definitions.go | 4 ++-- monitor_services_create_token.go | 2 +- network_ips.go | 2 +- object_storage_bucket_certs.go | 2 +- object_storage_buckets.go | 4 ++-- object_storage_endpoints.go | 2 +- profile.go | 2 +- profile_logins.go | 2 +- profile_sshkeys.go | 4 ++-- profile_tokens.go | 4 ++-- regions_availability.go | 2 +- retries.go | 4 ++-- tags.go | 2 +- test/integration/integration_suite_test.go | 2 +- volumes.go | 4 ++-- 35 files changed, 52 insertions(+), 50 deletions(-) diff --git a/account.go b/account.go index 8aee3cb91..9201fd62c 100644 --- a/account.go +++ b/account.go @@ -63,7 +63,7 @@ func (i Account) GetUpdateOptions() (o AccountUpdateOptions) { o.TaxID = i.TaxID o.Zip = i.Zip - return + return o } // UnmarshalJSON implements the json.Unmarshaler interface diff --git a/account_agreements.go b/account_agreements.go index d7437b4aa..795244a4a 100644 --- a/account_agreements.go +++ b/account_agreements.go @@ -22,7 +22,7 @@ func (i AccountAgreements) GetUpdateOptions() (o AccountAgreementsUpdateOptions) o.MasterServiceAgreement = i.MasterServiceAgreement o.PrivacyPolicy = i.PrivacyPolicy - return + return o } // GetAccountAgreements gets all agreements and their acceptance status for the Account. diff --git a/account_betas.go b/account_betas.go index 1a498e808..2e23c9284 100644 --- a/account_betas.go +++ b/account_betas.go @@ -8,7 +8,9 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// The details and enrollment information of a Beta program that an account is enrolled in. +// AccountBetaProgram represents an enrolled Account Beta Program object, +// which contains the details and enrollment information of a Beta program +// that an account is enrolled in. type AccountBetaProgram struct { Label string `json:"label"` ID string `json:"id"` diff --git a/account_maintenance.go b/account_maintenance.go index 96be1ad35..6e50cab5a 100644 --- a/account_maintenance.go +++ b/account_maintenance.go @@ -28,7 +28,7 @@ type AccountMaintenance struct { When *time.Time `json:"when"` } -// The entity being affected by maintenance +// Entity represents the entity being affected by maintenance type Entity struct { ID int `json:"id"` Label string `json:"label"` diff --git a/account_oauth_client.go b/account_oauth_client.go index ee0c48099..7b0e7795a 100644 --- a/account_oauth_client.go +++ b/account_oauth_client.go @@ -68,7 +68,7 @@ func (i OAuthClient) GetCreateOptions() (o OAuthClientCreateOptions) { o.Label = i.Label o.Public = i.Public - return + return o } // GetUpdateOptions converts a OAuthClient to OAuthClientUpdateOptions for use in UpdateOAuthClient @@ -77,7 +77,7 @@ func (i OAuthClient) GetUpdateOptions() (o OAuthClientUpdateOptions) { o.Label = i.Label o.Public = i.Public - return + return o } // ListOAuthClients lists OAuthClients diff --git a/account_payments.go b/account_payments.go index 40feebc13..af2488e9e 100644 --- a/account_payments.go +++ b/account_payments.go @@ -53,7 +53,7 @@ func (i *Payment) UnmarshalJSON(b []byte) error { // GetCreateOptions converts a Payment to PaymentCreateOptions for use in CreatePayment func (i Payment) GetCreateOptions() (o PaymentCreateOptions) { o.USD = i.USD - return + return o } // ListPayments lists Payments diff --git a/account_users.go b/account_users.go index d6e46ef09..13e71c520 100644 --- a/account_users.go +++ b/account_users.go @@ -98,7 +98,7 @@ func (i User) GetCreateOptions() (o UserCreateOptions) { o.Email = i.Email o.Restricted = i.Restricted - return + return o } // GetUpdateOptions converts a User to UserUpdateOptions for use in UpdateUser @@ -107,7 +107,7 @@ func (i User) GetUpdateOptions() (o UserUpdateOptions) { o.Restricted = copyBool(&i.Restricted) o.Email = i.Email - return + return o } // ListUsers lists Users on the account diff --git a/betas.go b/betas.go index 97a89ef5b..476ef351f 100644 --- a/betas.go +++ b/betas.go @@ -8,7 +8,7 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// Beta Program is a new product or service that is not generally available to all Akamai customers. +// BetaProgram is a new product or service that is not generally available to all Akamai customers. // Users must enroll into a beta in order to access the functionality. type BetaProgram struct { Label string `json:"label"` diff --git a/client.go b/client.go index 476151e48..bff9a0b78 100644 --- a/client.go +++ b/client.go @@ -45,7 +45,7 @@ const ( APIEnvVar = "LINODE_TOKEN" // APISecondsPerPoll how frequently to poll for new Events or Status in WaitFor functions APISecondsPerPoll = 3 - // Maximum wait time for retries + // APIRetryMaxWaitTime is the maximum wait time for retries APIRetryMaxWaitTime = time.Duration(30) * time.Second APIDefaultCacheExpiration = time.Minute * 15 ) @@ -171,7 +171,7 @@ func NewClient(hc *http.Client) (client Client) { SetDebug(envDebug). enableLogSanitization() - return + return client } // NewClientFromEnv creates a Client and initializes it with values diff --git a/client_monitor.go b/client_monitor.go index 33d42b161..b54dc8284 100644 --- a/client_monitor.go +++ b/client_monitor.go @@ -66,7 +66,7 @@ func NewMonitorClient(hc *http.Client) (mClient MonitorClient) { mClient.SetDebug(envDebug) - return + return mClient } // SetUserAgent sets a custom user-agent for HTTP requests diff --git a/domain_records.go b/domain_records.go index 373d797e7..2d8865e66 100644 --- a/domain_records.go +++ b/domain_records.go @@ -105,7 +105,7 @@ func (d DomainRecord) GetUpdateOptions() (du DomainRecordUpdateOptions) { du.TTLSec = d.TTLSec du.Tag = copyString(d.Tag) - return + return du } // ListDomainRecords lists DomainRecords diff --git a/domains.go b/domains.go index 7e04b4eed..4f2f2179b 100644 --- a/domains.go +++ b/domains.go @@ -190,7 +190,7 @@ func (d Domain) GetUpdateOptions() (du DomainUpdateOptions) { du.RefreshSec = d.RefreshSec du.TTLSec = d.TTLSec - return + return du } // ListDomains lists Domains diff --git a/firewall_devices.go b/firewall_devices.go index adbd9a17c..4ccc850b5 100644 --- a/firewall_devices.go +++ b/firewall_devices.go @@ -74,7 +74,7 @@ func (c *Client) GetFirewallDevice(ctx context.Context, firewallID, deviceID int return doGETRequest[FirewallDevice](ctx, c, e) } -// AddFirewallDevice associates a Device with a given Firewall +// CreateFirewallDevice associates a Device with a given Firewall func (c *Client) CreateFirewallDevice(ctx context.Context, firewallID int, opts FirewallDeviceCreateOptions) (*FirewallDevice, error) { e := formatAPIPath("networking/firewalls/%d/devices", firewallID) return doPOSTRequest[FirewallDevice](ctx, c, e, opts) diff --git a/firewall_templates.go b/firewall_templates.go index e3be54fda..e2f5f6702 100644 --- a/firewall_templates.go +++ b/firewall_templates.go @@ -9,15 +9,15 @@ type FirewallTemplate struct { Rules FirewallRuleSet `json:"rules"` } -// NOTE: This feature may not currently be available to all users. // GetFirewallTemplate gets a FirewallTemplate given a slug. +// NOTE: This feature may not currently be available to all users. func (c *Client) GetFirewallTemplate(ctx context.Context, slug string) (*FirewallTemplate, error) { e := formatAPIPath("networking/firewalls/templates/%s", slug) return doGETRequest[FirewallTemplate](ctx, c, e) } -// NOTE: This feature may not currently be available to all users. // ListFirewallTemplates gets all available firewall templates for the account. +// NOTE: This feature may not currently be available to all users. func (c *Client) ListFirewallTemplates(ctx context.Context, opts *ListOptions) ([]FirewallTemplate, error) { return getPaginatedResults[FirewallTemplate](ctx, c, "networking/firewalls/templates", opts) } diff --git a/images.go b/images.go index 858136a41..21b2853fb 100644 --- a/images.go +++ b/images.go @@ -142,7 +142,7 @@ func (i Image) GetUpdateOptions() (iu ImageUpdateOptions) { iu.Label = i.Label iu.Description = copyString(&i.Description) - return + return iu } // ListImages lists Images. diff --git a/instance_ips.go b/instance_ips.go index cd87dbcd7..cd2a78505 100644 --- a/instance_ips.go +++ b/instance_ips.go @@ -145,7 +145,7 @@ func (c *Client) DeleteInstanceIPAddress(ctx context.Context, linodeID int, ipAd return doDELETERequest(ctx, c, e) } -// Function to add additional reserved IPV4 addresses to an existing linode +// AssignInstanceReservedIP adds additional reserved IPV4 addresses to an existing linode func (c *Client) AssignInstanceReservedIP(ctx context.Context, linodeID int, opts InstanceReserveIPOptions) (*InstanceIP, error) { endpoint := formatAPIPath("linode/instances/%d/ips", linodeID) return doPOSTRequest[InstanceIP](ctx, c, endpoint, opts) diff --git a/lke_clusters.go b/lke_clusters.go index f5a56fccd..786b6faaf 100644 --- a/lke_clusters.go +++ b/lke_clusters.go @@ -158,7 +158,7 @@ func (i LKECluster) GetCreateOptions() (o LKEClusterCreateOptions) { } // @TODO copy NodePools? - return + return o } // GetUpdateOptions converts a LKECluster to LKEClusterUpdateOptions for use in UpdateLKECluster @@ -174,7 +174,7 @@ func (i LKECluster) GetUpdateOptions() (o LKEClusterUpdateOptions) { // ACL will not be populated in the control plane response } - return + return o } // ListLKEVersions lists the Kubernetes versions available through LKE. This endpoint is cached by default. diff --git a/lke_node_pools.go b/lke_node_pools.go index 62cce2ced..fc90ed25d 100644 --- a/lke_node_pools.go +++ b/lke_node_pools.go @@ -132,7 +132,7 @@ func (l LKENodePool) GetCreateOptions() (o LKENodePoolCreateOptions) { o.UpdateStrategy = l.UpdateStrategy o.Label = l.Label - return + return o } // GetUpdateOptions converts a LKENodePool to LKENodePoolUpdateOptions for use in UpdateLKENodePoolUpdate @@ -146,7 +146,7 @@ func (l LKENodePool) GetUpdateOptions() (o LKENodePoolUpdateOptions) { o.UpdateStrategy = l.UpdateStrategy o.Label = l.Label - return + return o } // ListLKENodePools lists LKENodePools diff --git a/longview.go b/longview.go index 14d8fb41b..16be227e3 100644 --- a/longview.go +++ b/longview.go @@ -28,7 +28,7 @@ type LongviewClientCreateOptions struct { Label string `json:"label"` } -// LongviewClientCreateOptions is an options struct used when Updating a Longview Client +// LongviewClientUpdateOptions is an options struct used when Updating a Longview Client type LongviewClientUpdateOptions struct { Label string `json:"label"` } diff --git a/monitor_dashboards.go b/monitor_dashboards.go index 2554a7b59..2492e1ef6 100644 --- a/monitor_dashboards.go +++ b/monitor_dashboards.go @@ -19,7 +19,7 @@ type MonitorDashboard struct { Widgets []DashboardWidget `json:"widgets"` } -// enum object for serviceType +// ServiceType is an enum object for serviceType type ServiceType string const ( @@ -33,7 +33,7 @@ const ( ServiceTypeFirewallService ServiceType = "firewall" ) -// enum object for DashboardType +// DashboardType is an enum object for DashboardType type DashboardType string const ( @@ -53,7 +53,7 @@ type DashboardWidget struct { AggregateFunction AggregateFunction `json:"aggregate_function"` } -// Enum object for AggregateFunction +// AggregateFunction is an enum object for AggregateFunction type AggregateFunction string const ( @@ -67,7 +67,7 @@ const ( AggregateFunctionLast AggregateFunction = "last" ) -// Enum object for Chart type +// ChartType is an enum object for Chart type type ChartType string const ( diff --git a/monitor_metrics_definitions.go b/monitor_metrics_definitions.go index eca52b112..026652a00 100644 --- a/monitor_metrics_definitions.go +++ b/monitor_metrics_definitions.go @@ -16,7 +16,7 @@ type MonitorMetricsDefinition struct { Unit MetricUnit `json:"unit"` } -// Enum object for MetricType +// MetricType is an enum object for MetricType type MetricType string const ( @@ -26,7 +26,7 @@ const ( MetricTypeSummary MetricType = "summary" ) -// Enum object for Unit +// MetricUnit is an enum object for Unit type MetricUnit string const ( diff --git a/monitor_services_create_token.go b/monitor_services_create_token.go index a5cb47ae9..a4c679e05 100644 --- a/monitor_services_create_token.go +++ b/monitor_services_create_token.go @@ -9,7 +9,7 @@ type MonitorServiceToken struct { Token string `json:"token"` } -// Create token options. +// MonitorTokenCreateOptions contains create token options. type MonitorTokenCreateOptions struct { // EntityIDs are expected to be type "any" as different service_types have different variable type for their entity_ids. For example, Linode has "int" entity_ids whereas object storage has "string" as entity_ids. EntityIDs []any `json:"entity_ids"` diff --git a/network_ips.go b/network_ips.go index 8cebabb42..72be24c63 100644 --- a/network_ips.go +++ b/network_ips.go @@ -69,7 +69,7 @@ func (i InstanceIP) GetUpdateOptionsV2() IPAddressUpdateOptionsV2 { // Deprecated: Please use GetUpdateOptionsV2 for all new implementations. func (i InstanceIP) GetUpdateOptions() (o IPAddressUpdateOptions) { o.RDNS = copyString(&i.RDNS) - return + return o } // ListIPAddresses lists IPAddresses. diff --git a/object_storage_bucket_certs.go b/object_storage_bucket_certs.go index 6597e40f5..4f52859ca 100644 --- a/object_storage_bucket_certs.go +++ b/object_storage_bucket_certs.go @@ -36,7 +36,7 @@ func (c *Client) GetObjectStorageBucketCert(ctx context.Context, clusterOrRegion return doGETRequest[ObjectStorageBucketCert](ctx, c, e) } -// UploadObjectStorageBucketCert uploads a TLS/SSL Cert to be used with an Object Storage Bucket. +// UploadObjectStorageBucketCertV2 uploads a TLS/SSL Cert to be used with an Object Storage Bucket. func (c *Client) UploadObjectStorageBucketCertV2( ctx context.Context, clusterOrRegionID, bucket string, diff --git a/object_storage_buckets.go b/object_storage_buckets.go index 8755f3ade..bdcb54229 100644 --- a/object_storage_buckets.go +++ b/object_storage_buckets.go @@ -159,7 +159,7 @@ func (c *Client) UpdateObjectStorageBucketAccess(ctx context.Context, clusterOrR return doPOSTRequestNoResponseBody(ctx, c, e, opts) } -// GetObjectStorageBucketAccess gets the current access config for a bucket +// GetObjectStorageBucketAccessV2 gets the current access config for a bucket func (c *Client) GetObjectStorageBucketAccessV2(ctx context.Context, clusterOrRegionID, label string) (*ObjectStorageBucketAccessV2, error) { e := formatAPIPath("object-storage/buckets/%s/%s/access", clusterOrRegionID, label) return doGETRequest[ObjectStorageBucketAccessV2](ctx, c, e) @@ -171,7 +171,7 @@ func (c *Client) DeleteObjectStorageBucket(ctx context.Context, clusterOrRegionI return doDELETERequest(ctx, c, e) } -// Lists the contents of the specified ObjectStorageBucket +// ListObjectStorageBucketContents lists the contents of the specified ObjectStorageBucket func (c *Client) ListObjectStorageBucketContents( ctx context.Context, clusterOrRegionID, label string, diff --git a/object_storage_endpoints.go b/object_storage_endpoints.go index 635091948..f3ad166c6 100644 --- a/object_storage_endpoints.go +++ b/object_storage_endpoints.go @@ -2,7 +2,7 @@ package linodego import "context" -// NotificationType constants start with Notification and include all known Linode API Notification Types. +// ObjectStorageEndpointType constants start with Notification and include all known Linode API Notification Types. type ObjectStorageEndpointType string // NotificationType constants represent the actions that cause a Notification. New types may be added in the future. diff --git a/profile.go b/profile.go index ec227b6d1..c290d62b5 100644 --- a/profile.go +++ b/profile.go @@ -66,7 +66,7 @@ func (i Profile) GetUpdateOptions() (o ProfileUpdateOptions) { o.TwoFactorAuth = copyBool(&i.TwoFactorAuth) o.Restricted = copyBool(&i.Restricted) - return + return o } // GetProfile returns the Profile of the authenticated user diff --git a/profile_logins.go b/profile_logins.go index 76f0f201f..96740a398 100644 --- a/profile_logins.go +++ b/profile_logins.go @@ -8,7 +8,7 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// Profile represents a Profile object +// ProfileLogin represents a Profile object type ProfileLogin struct { Datetime *time.Time `json:"datetime"` ID int `json:"id"` diff --git a/profile_sshkeys.go b/profile_sshkeys.go index 30b0e4bfe..a18566fdb 100644 --- a/profile_sshkeys.go +++ b/profile_sshkeys.go @@ -53,13 +53,13 @@ func (i SSHKey) GetCreateOptions() (o SSHKeyCreateOptions) { o.Label = i.Label o.SSHKey = i.SSHKey - return + return o } // GetUpdateOptions converts a SSHKey to SSHKeyCreateOptions for use in UpdateSSHKey func (i SSHKey) GetUpdateOptions() (o SSHKeyUpdateOptions) { o.Label = i.Label - return + return o } // ListSSHKeys lists SSHKeys diff --git a/profile_tokens.go b/profile_tokens.go index 958bedaec..0622ec1d3 100644 --- a/profile_tokens.go +++ b/profile_tokens.go @@ -77,13 +77,13 @@ func (i Token) GetCreateOptions() (o TokenCreateOptions) { o.Expiry = copyTime(i.Expiry) o.Scopes = i.Scopes - return + return o } // GetUpdateOptions converts a Token to TokenUpdateOptions for use in UpdateToken func (i Token) GetUpdateOptions() (o TokenUpdateOptions) { o.Label = i.Label - return + return o } // ListTokens lists Tokens diff --git a/regions_availability.go b/regions_availability.go index 4caefeb74..dcba07f75 100644 --- a/regions_availability.go +++ b/regions_availability.go @@ -4,7 +4,7 @@ import ( "context" ) -// Region represents a linode region object +// RegionAvailability represents a linode region object type RegionAvailability struct { Region string `json:"region"` Plan string `json:"plan"` diff --git a/retries.go b/retries.go index 8831b5200..047fed84e 100644 --- a/retries.go +++ b/retries.go @@ -18,10 +18,10 @@ const ( defaultRetryCount = 1000 ) -// type RetryConditional func(r *resty.Response) (shouldRetry bool) +// RetryConditional func(r *resty.Response) (shouldRetry bool) type RetryConditional resty.RetryConditionFunc -// type RetryAfter func(c *resty.Client, r *resty.Response) (time.Duration, error) +// RetryAfter func(c *resty.Client, r *resty.Response) (time.Duration, error) type RetryAfter resty.RetryAfterFunc // Configures resty to diff --git a/tags.go b/tags.go index 2d37ca219..e23ba2168 100644 --- a/tags.go +++ b/tags.go @@ -47,7 +47,7 @@ type TagCreateOptions struct { // GetCreateOptions converts a Tag to TagCreateOptions for use in CreateTag func (i Tag) GetCreateOptions() (o TagCreateOptions) { o.Label = i.Label - return + return o } // ListTags lists Tags diff --git a/test/integration/integration_suite_test.go b/test/integration/integration_suite_test.go index ae8a9fcd2..3ca0cb8a2 100644 --- a/test/integration/integration_suite_test.go +++ b/test/integration/integration_suite_test.go @@ -127,7 +127,7 @@ func testRecorder(t *testing.T, fixturesYaml string, testingMode recorder.Mode, recordStopper = func() { r.Stop() } - return + return r, recordStopper } // createTestClient is a testing helper to creates a linodego.Client initialized using diff --git a/volumes.go b/volumes.go index 70fe3da94..820b0f460 100644 --- a/volumes.go +++ b/volumes.go @@ -99,7 +99,7 @@ func (v Volume) GetUpdateOptions() (updateOpts VolumeUpdateOptions) { updateOpts.Label = v.Label updateOpts.Tags = &v.Tags - return + return updateOpts } // GetCreateOptions converts a Volume to VolumeCreateOptions for use in CreateVolume @@ -113,7 +113,7 @@ func (v Volume) GetCreateOptions() (createOpts VolumeCreateOptions) { createOpts.LinodeID = *v.LinodeID } - return + return createOpts } // ListVolumes lists Volumes From cf6fd9b344a37a33734a1436fce4f826cd25cdf9 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Thu, 25 Sep 2025 14:58:45 -0400 Subject: [PATCH 22/24] Fix firewall device for linode interfaces (#829) --- firewall_devices.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firewall_devices.go b/firewall_devices.go index 4ccc850b5..9b2dda688 100644 --- a/firewall_devices.go +++ b/firewall_devices.go @@ -13,9 +13,9 @@ type FirewallDeviceType string // FirewallDeviceType constants start with FirewallDevice const ( - FirewallDeviceLinode FirewallDeviceType = "linode" - FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer" - FirewallDeviceInterface FirewallDeviceType = "interface" + FirewallDeviceLinode FirewallDeviceType = "linode" + FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer" + FirewallDeviceLinodeInterface FirewallDeviceType = "linode_interface" ) // FirewallDevice represents a device governed by a Firewall From f1a8f6c4f676a6c0b1daed08cd86c65bcab628b7 Mon Sep 17 00:00:00 2001 From: srbhaakamai Date: Mon, 13 Oct 2025 13:50:56 +0530 Subject: [PATCH 23/24] Update test/integration/monitor_alert_definitions_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../monitor_alert_definitions_test.go | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index eef9684d8..030de7383 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -165,9 +165,24 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { // Clean up created alert definition if createdAlert != nil { - // Wait for 2 minutes before deletion for update to complete - time.Sleep(2 * time.Minute) - err = client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID) - assert.NoError(t, err) + // Retry deletion with exponential backoff for up to 2 minutes + maxWait := 2 * time.Minute + baseDelay := 2 * time.Second + var lastErr error + start := time.Now() + for attempt := 0; time.Since(start) < maxWait; attempt++ { + err = client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID) + if err == nil { + break + } + lastErr = err + // Exponential backoff, capped at 30s + sleep := baseDelay * (1 << attempt) + if sleep > 30*time.Second { + sleep = 30 * time.Second + } + time.Sleep(sleep) + } + assert.NoError(t, err, "DeleteMonitorAlertDefinition failed after retries: %v", lastErr) } } From 4086c74155757210710b2feed901b7994b8d717c Mon Sep 17 00:00:00 2001 From: Srinidhi Bhat Date: Mon, 13 Oct 2025 16:27:25 +0530 Subject: [PATCH 24/24] DI-27070 Updated Go SDK to align with Python SDK for Monitor Alert Definitions - Updated AlertChannel struct to match Python SDK structure - Fixed API endpoints to use correct monitor/alert-channels path - Added CriteriaCondition enum for alert definition criteria - Ensured field naming consistency between Go and Python SDKs - Fixed integration test to use ListAlertChannels instead of non-existent GetAlertChannels - All unit tests and integration tests pass successfully - Full CRUD operations validated with real API --- alert_channels.go | 33 ++++++++----------- monitor_alert_definitions.go | 7 ++++ monitor_dashboards.go | 2 +- .../monitor_alert_definitions_test.go | 12 +++---- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/alert_channels.go b/alert_channels.go index d609fd178..0a7cc4224 100644 --- a/alert_channels.go +++ b/alert_channels.go @@ -14,17 +14,15 @@ type AlertChannelEnvelope struct { // AlertChannel represents a Monitor Channel object. type AlertChannel struct { - ID int `json:"id"` - Alerts []AlertChannelEnvelope `json:"alerts"` - Label string `json:"label"` - Channel_type string `json:"channel_type"` - Content ChannelContent `json:"content"` - Type AlertType `json:"type"` - Details AlertChannelDetail `json:"details"` - Created string `json:"created"` - Created_by string `json:"created_by"` - Updated string `json:"updated"` - Updated_by string `json:"updated_by"` + ID int `json:"id"` + Label string `json:"label"` + ChannelType string `json:"channel_type"` + Content ChannelContent `json:"content"` + Created string `json:"created"` + CreatedBy string `json:"created_by"` + Updated string `json:"updated"` + UpdatedBy string `json:"updated_by"` + URL string `json:"url"` } // AlertChannelDetail represents the details of a Monitor Channel. @@ -61,19 +59,14 @@ type ChannelContent struct { // Other channel types like 'webhook', 'slack' could be added here as optional fields. } -// ListMonitorChannels gets a paginated list of Monitor Channels. +// ListAlertChannels gets a paginated list of Alert Channels. func (c *Client) ListAlertChannels(ctx context.Context, opts *ListOptions) ([]AlertChannel, error) { - endpoint := formatAPIV4BetaPath("monitor/channels") + endpoint := formatAPIV4BetaPath("monitor/alert-channels") return getPaginatedResults[AlertChannel](ctx, c, endpoint, opts) } -// GetMonitorChannel gets a Monitor Channel by ID. +// GetAlertChannel gets an Alert Channel by ID. func (c *Client) GetAlertChannel(ctx context.Context, channelID int) (*AlertChannel, error) { - e := formatAPIV4BetaPath("monitor/channels/%d", channelID) - return doGETRequest[AlertChannel](ctx, c, e) -} - -func (c *Client) GetAlertChannels(ctx context.Context) (*AlertChannel, error) { - e := formatAPIV4BetaPath("monitor/alert-channels/") + e := formatAPIV4BetaPath("monitor/alert-channels/%d", channelID) return doGETRequest[AlertChannel](ctx, c, e) } diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index ba842d7a5..9e3339234 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -84,6 +84,13 @@ const ( SeverityInfo Severity = 3 ) +// CriteriaCondition represents supported criteria conditions +type CriteriaCondition string + +const ( + CriteriaConditionAll CriteriaCondition = "ALL" +) + // AlertDefinitionCreateOptions are the options used to create a new alert definition. type AlertDefinitionCreateOptions struct { ServiceType string `json:"service_type"` // mandatory diff --git a/monitor_dashboards.go b/monitor_dashboards.go index 2492e1ef6..18d8d7ada 100644 --- a/monitor_dashboards.go +++ b/monitor_dashboards.go @@ -28,7 +28,7 @@ const ( ServiceTypeDBaaS ServiceType = "dbaas" ServiceTypeACLB ServiceType = "aclb" ServiceTypeNodeBalancer ServiceType = "nodebalancer" - ServiceTypeObjectStorage ServiceType = "objectstorage" + ServiceTypeObjectStorage ServiceType = "object_storage" ServiceTypeVPC ServiceType = "vpc" ServiceTypeFirewallService ServiceType = "firewall" ) diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index 030de7383..6fef65d2b 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -53,14 +53,14 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { fetchedChannelID = alerts[0].AlertChannels[0].ID fetchedChannelLabel = alerts[0].AlertChannels[0].Label } else { - // Fallback to GetAlertChannels (some fixtures expose a single alert-channel endpoint) - fetchedChannelResp, err := client.GetAlertChannels(context.Background()) - if err != nil { + // Fallback to ListAlertChannels to get available channels + channels, err := client.ListAlertChannels(context.Background(), nil) + if err != nil || len(channels) == 0 { t.Fatalf("failed to determine a monitor channel to use: %s", err) } - channelID = fetchedChannelResp.ID - fetchedChannelID = fetchedChannelResp.ID - fetchedChannelLabel = fetchedChannelResp.Label + channelID = channels[0].ID + fetchedChannelID = channels[0].ID + fetchedChannelLabel = channels[0].Label } // Validate the chosen channel assert.NotZero(t, fetchedChannelID, "fetchedChannel.ID should not be zero")