{"record":{"id":"801546d18ef104d7","repo":"kubernetes/kops","slug":"instance-group-q-neither-control-plane-nor-api-se","errorCode":null,"errorMessage":"instance group %q neither control-plane nor api-server","messagePattern":"instance group %q neither control-plane nor api-server","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/model/gcemodel/api_loadbalancer.go","lineNumber":184,"sourceCode":"\tfor _, ig := range b.InstanceGroups {\n\t\tif !ig.RunsAPIServer() {\n\t\t\tcontinue\n\t\t}\n\t\tif len(ig.Spec.Zones) > 1 {\n\t\t\treturn fmt.Errorf(\"instance group %q has %d zones, which is not yet supported for GCP\", ig.GetName(), len(ig.Spec.Zones))\n\t\t}\n\t\tif len(ig.Spec.Zones) == 0 {\n\t\t\treturn fmt.Errorf(\"instance group %q must specify exactly one zone\", ig.GetName())\n\t\t}\n\t\tzone := ig.Spec.Zones[0]\n\t\tigm := &gcetasks.InstanceGroupManager{Name: s(gce.NameForInstanceGroupManager(b.Cluster.ObjectMeta.Name, ig.ObjectMeta.Name, zone)), Zone: s(zone)}\n\t\tapiIGMs = append(apiIGMs, igm)\n\t\tif ig.IsControlPlane() {\n\t\t\tcontrolPlaneIGMs = append(controlPlaneIGMs, igm)\n\t\t} else if ig.IsAPIServerOnly() {\n\t\t\trequireEtcdLB = b.Cluster.UsesNoneDNS()\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"instance group %q neither control-plane nor api-server\", ig.GetName())\n\t\t}\n\t}\n\tbackendService := &gcetasks.BackendService{\n\t\tName:                  s(b.NameForBackendService(\"api\")),\n\t\tProtocol:              s(\"TCP\"),\n\t\tHealthChecks:          []*gcetasks.HealthCheck{hc},\n\t\tLifecycle:             b.Lifecycle,\n\t\tLoadBalancingScheme:   s(\"INTERNAL\"),\n\t\tInstanceGroupManagers: apiIGMs,\n\t}\n\tc.AddTask(backendService)\n\n\t// controlPlaneBS is a backend service that only targets ControlPlane MIGs.\n\t// It is used for kops-controller and etcd forwarding rules, which only run\n\t// on ControlPlane nodes. When there are no dedicated APIServer IGs, this is\n\t// the same set of backends as the API backend service.\n\tcontrolPlaneBS := backendService\n\tif b.HasAPIServerOnlyInstanceGroups() {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/model/gcemodel/api_loadbalancer.go#L166-L202","documentation":"createInternalLB (pkg/model/gcemodel/api_loadbalancer.go:184) classifies each API-serving instance group as control-plane or API-server-only; anything else cannot be attached to the API load balancer and is rejected. This catches specs where a non-control-plane, non-apiserver IG (e.g. a plain Node IG) is configured in a way that makes RunsAPIServer() true, or a role the classifier does not recognize.","triggerScenarios":"Looping over b.InstanceGroups, an IG with RunsAPIServer() true is neither IsControlPlane() nor IsAPIServerOnly() — typically a Node-role IG with an api-server role override, or a corrupted/legacy IG spec with role combinations the current kOps cannot classify.","commonSituations":"Hand-edited manifests mixing role fields; migrating clusters between kOps versions where the APIServer-only role handling changed; tooling that emits instance groups with unexpected role values.","solutions":["Inspect the named IG's spec.role and fix it to ControlPlane (Master), Node, or APIServer as intended","If a Node IG is wrongly marked as API-serving (e.g. kubelet api-server related flags), revert it via 'kops edit ig'","Regenerate the IG with 'kops create ig' rather than hand-editing internal fields","Upgrade/downgrade kOps to a version matching the spec's role semantics"],"exampleFix":"// before\nmetadata:\n  name: nodes\nspec:\n  role: Node\n  machineType: n1-standard-2\n  # plus stale apiserver-role fields left from a template\n// after\nmetadata:\n  name: apiserver\nspec:\n  role: APIServer\n  machineType: n1-standard-2","handlingStrategy":"validation","validationCode":"for _, ig := range instanceGroups {\n\tif ig.RunsAPIServer() && !ig.IsControlPlane() && !ig.IsAPIServerOnly() {\n\t\treturn fmt.Errorf(\"IG %q is API-serving but role %q is neither ControlPlane nor APIServer\", ig.Name, ig.Spec.Role)\n\t}\n}","typeGuard":"func classifiableAPIIG(ig *kops.InstanceGroup) bool {\n\treturn !ig.RunsAPIServer() || ig.IsControlPlane() || ig.IsAPIServerOnly()\n}","tryCatchPattern":null,"preventionTips":["Use only standard roles (ControlPlane/Master, Node, APIServer) in GCP specs","Avoid merging IG specs across clusters with kOps versions differing in role semantics","Audit generated manifests for stale role fields after template changes","Keep Node IGs free of API-server role markers"],"tags":["kops","gcp","instance-group","roles"],"backgroundTag":"invalid-instance-group-role-combination","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}