Skip to content

Commit ebe9d3d

Browse files
committed
** CAPG machines should also attept to use the default service account as a last resort. This happens when the user has started an install with the default service account.
** Add the missing firewall rules for etcd and health-checks.
1 parent ce05d95 commit ebe9d3d

File tree

5 files changed

+180
-27
lines changed

5 files changed

+180
-27
lines changed

pkg/asset/machines/gcp/gcpmachines.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
package gcp
33

44
import (
5+
"context"
6+
"encoding/json"
57
"fmt"
68

79
"github.com/sirupsen/logrus"
@@ -14,6 +16,7 @@ import (
1416

1517
"github.com/openshift/installer/pkg/asset"
1618
"github.com/openshift/installer/pkg/asset/installconfig"
19+
gcpconfig "github.com/openshift/installer/pkg/asset/installconfig/gcp"
1720
gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
1821
"github.com/openshift/installer/pkg/types"
1922
gcptypes "github.com/openshift/installer/pkg/types/gcp"
@@ -49,7 +52,10 @@ func GenerateMachines(installConfig *installconfig.InstallConfig, infraID string
4952
// Create GCP and CAPI machines for all master replicas in pool
5053
for idx := int64(0); idx < total; idx++ {
5154
name := fmt.Sprintf("%s-%s-%d", infraID, pool.Name, idx)
52-
gcpMachine := createGCPMachine(name, installConfig, infraID, mpool, imageName)
55+
gcpMachine, err := createGCPMachine(name, installConfig, infraID, mpool, imageName)
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to create control plane (%d): %w", idx, err)
58+
}
5359

5460
result = append(result, &asset.RuntimeFile{
5561
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", gcpMachine.Name)},
@@ -83,7 +89,10 @@ func GenerateBootstrapMachines(name string, installConfig *installconfig.Install
8389
mpool := pool.Platform.GCP
8490

8591
// Create one GCP and CAPI machine for bootstrap
86-
bootstrapGCPMachine := createGCPMachine(name, installConfig, infraID, mpool, imageName)
92+
bootstrapGCPMachine, err := createGCPMachine(name, installConfig, infraID, mpool, imageName)
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to create bootstrap machine: %w", err)
95+
}
8796

8897
// Identify this as a bootstrap machine
8998
bootstrapGCPMachine.Labels["install.openshift.io/bootstrap"] = ""
@@ -107,7 +116,7 @@ func GenerateBootstrapMachines(name string, installConfig *installconfig.Install
107116
}
108117

109118
// Create a CAPG-specific machine.
110-
func createGCPMachine(name string, installConfig *installconfig.InstallConfig, infraID string, mpool *gcptypes.MachinePool, imageName string) *capg.GCPMachine {
119+
func createGCPMachine(name string, installConfig *installconfig.InstallConfig, infraID string, mpool *gcptypes.MachinePool, imageName string) (*capg.GCPMachine, error) {
111120
// Use the rhcosImage as image name if not defined
112121
osImage := imageName
113122
if mpool.OSImage != nil {
@@ -165,8 +174,29 @@ func createGCPMachine(name string, installConfig *installconfig.InstallConfig, i
165174
// value in install-config.
166175
// Note - the derivation of the ServiceAccount from credentials will no longer be supported.
167176
if len(installConfig.Config.Platform.GCP.NetworkProjectID) > 0 {
168-
if mpool.ServiceAccount != "" {
169-
serviceAccount.Email = mpool.ServiceAccount
177+
serviceAccount.Email = mpool.ServiceAccount
178+
if serviceAccount.Email == "" {
179+
sess, err := gcpconfig.GetSession(context.TODO())
180+
if err != nil {
181+
return nil, fmt.Errorf("gcp machine creation failed to get session: %w", err)
182+
}
183+
184+
// The JSON can be `nil` if auth is provided from env
185+
// https://pkg.go.dev/golang.org/x/[email protected]/google#Credentials
186+
if len(sess.Credentials.JSON) == 0 {
187+
return nil, fmt.Errorf("could not extract service account from loaded credentials. Please specify a service account to be used for shared vpc installations in the install-config.yaml")
188+
}
189+
190+
var found bool
191+
sa := make(map[string]interface{})
192+
err = json.Unmarshal(sess.Credentials.JSON, &sa)
193+
if err != nil {
194+
return nil, fmt.Errorf("failed to unmarshal gcp session: %w", err)
195+
}
196+
serviceAccount.Email, found = sa["client_email"].(string)
197+
if !found {
198+
return nil, fmt.Errorf("could not find google service account")
199+
}
170200
}
171201
}
172202
gcpMachine.Spec.ServiceAccount = serviceAccount
@@ -184,7 +214,7 @@ func createGCPMachine(name string, installConfig *installconfig.InstallConfig, i
184214
gcpMachine.Spec.RootDiskEncryptionKey = encryptionKey
185215
}
186216

187-
return gcpMachine
217+
return gcpMachine, nil
188218
}
189219

190220
// Create a CAPI machine based on the CAPG machine.

pkg/asset/manifests/gcp/cluster.go

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package gcp
22

33
import (
4+
"context"
45
"fmt"
56
"net"
7+
"time"
68

79
"github.com/apparentlymart/go-cidr/cidr"
10+
"google.golang.org/api/compute/v1"
11+
"google.golang.org/api/option"
812
corev1 "k8s.io/api/core/v1"
913
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1014
"k8s.io/apimachinery/pkg/util/sets"
@@ -14,6 +18,7 @@ import (
1418

1519
"github.com/openshift/installer/pkg/asset"
1620
"github.com/openshift/installer/pkg/asset/installconfig"
21+
gcpic "github.com/openshift/installer/pkg/asset/installconfig/gcp"
1722
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
1823
gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
1924
"github.com/openshift/installer/pkg/types/gcp"
@@ -33,26 +38,42 @@ func GenerateClusterAssets(installConfig *installconfig.InstallConfig, clusterID
3338
networkName = installConfig.Config.GCP.Network
3439
}
3540

36-
masterSubnet := gcp.DefaultSubnetName(clusterID.InfraID, "master")
41+
controlPlaneSubnetName := gcp.DefaultSubnetName(clusterID.InfraID, "master")
42+
controlPlaneSubnetCidr := ""
3743
if installConfig.Config.GCP.ControlPlaneSubnet != "" {
38-
masterSubnet = installConfig.Config.GCP.ControlPlaneSubnet
44+
controlPlaneSubnetName = installConfig.Config.GCP.ControlPlaneSubnet
45+
46+
controlPlaneSubnet, err := getSubnet(context.TODO(), installConfig.Config.GCP.NetworkProjectID, installConfig.Config.GCP.Region, controlPlaneSubnetName)
47+
if err != nil {
48+
return nil, fmt.Errorf("failed to get control plane subnet: %w", err)
49+
}
50+
// IpCidr is the IPv4 version, the IPv6 version can be accessed as well
51+
controlPlaneSubnetCidr = controlPlaneSubnet.IpCidrRange
3952
}
4053

41-
master := capg.SubnetSpec{
42-
Name: masterSubnet,
43-
CidrBlock: "",
54+
controlPlane := capg.SubnetSpec{
55+
Name: controlPlaneSubnetName,
56+
CidrBlock: controlPlaneSubnetCidr,
4457
Description: ptr.To(description),
4558
Region: installConfig.Config.GCP.Region,
4659
}
4760

48-
workerSubnet := gcp.DefaultSubnetName(clusterID.InfraID, "worker")
61+
computeSubnetName := gcp.DefaultSubnetName(clusterID.InfraID, "worker")
62+
computeSubnetCidr := ""
4963
if installConfig.Config.GCP.ComputeSubnet != "" {
50-
workerSubnet = installConfig.Config.GCP.ComputeSubnet
64+
computeSubnetName = installConfig.Config.GCP.ComputeSubnet
65+
66+
computeSubnet, err := getSubnet(context.TODO(), installConfig.Config.GCP.NetworkProjectID, installConfig.Config.GCP.Region, computeSubnetName)
67+
if err != nil {
68+
return nil, fmt.Errorf("failed to get compute subnet: %w", err)
69+
}
70+
// IpCidr is the IPv4 version, the IPv6 version can be accessed as well
71+
computeSubnetCidr = computeSubnet.IpCidrRange
5172
}
5273

53-
worker := capg.SubnetSpec{
54-
Name: workerSubnet,
55-
CidrBlock: "",
74+
compute := capg.SubnetSpec{
75+
Name: computeSubnetName,
76+
CidrBlock: computeSubnetCidr,
5677
Description: ptr.To(description),
5778
Region: installConfig.Config.GCP.Region,
5879
}
@@ -79,18 +100,18 @@ func GenerateClusterAssets(installConfig *installconfig.InstallConfig, clusterID
79100
if err != nil {
80101
return nil, fmt.Errorf("failed to create the master subnet %w", err)
81102
}
82-
master.CidrBlock = masterCIDR.String()
103+
controlPlane.CidrBlock = masterCIDR.String()
83104
}
84105

85106
if installConfig.Config.GCP.ComputeSubnet == "" {
86-
workerCIDR, err := cidr.Subnet(ipv4Net, 1, 1)
107+
computeCIDR, err := cidr.Subnet(ipv4Net, 1, 1)
87108
if err != nil {
88-
return nil, fmt.Errorf("failed to create the worker subnet %w", err)
109+
return nil, fmt.Errorf("failed to create the compute subnet %w", err)
89110
}
90-
worker.CidrBlock = workerCIDR.String()
111+
compute.CidrBlock = computeCIDR.String()
91112
}
92113

93-
subnets := []capg.SubnetSpec{master, worker}
114+
subnets := []capg.SubnetSpec{controlPlane, compute}
94115
// Subnets should never be auto created, even in shared VPC installs
95116
autoCreateSubnets := false
96117

@@ -126,6 +147,11 @@ func GenerateClusterAssets(installConfig *installconfig.InstallConfig, clusterID
126147
}
127148
gcpCluster.SetGroupVersionKind(capg.GroupVersion.WithKind("GCPCluster"))
128149

150+
// Set the network project during shared vpc installs
151+
if installConfig.Config.GCP.NetworkProjectID != "" {
152+
gcpCluster.Spec.Network.HostProject = ptr.To(installConfig.Config.GCP.NetworkProjectID)
153+
}
154+
129155
manifests = append(manifests, &asset.RuntimeFile{
130156
Object: gcpCluster,
131157
File: asset.File{Filename: "02_gcp-cluster.yaml"},
@@ -182,3 +208,30 @@ func findFailureDomains(installConfig *installconfig.InstallConfig) []string {
182208

183209
return zones.UnsortedList()
184210
}
211+
212+
// getSubnet will find a subnet in a project by the name. The matching subnet structure will be returned if
213+
// one is found.
214+
func getSubnet(ctx context.Context, project, region, subnetName string) (*compute.Subnetwork, error) {
215+
ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
216+
defer cancel()
217+
218+
ssn, err := gcpic.GetSession(ctx)
219+
if err != nil {
220+
return nil, fmt.Errorf("failed to get session: %w", err)
221+
}
222+
223+
computeService, err := compute.NewService(ctx, option.WithCredentials(ssn.Credentials))
224+
if err != nil {
225+
return nil, fmt.Errorf("failed to create compute service: %w", err)
226+
}
227+
228+
subnetService := compute.NewSubnetworksService(computeService)
229+
subnet, err := subnetService.Get(project, region, subnetName).Context(ctx).Do()
230+
if err != nil {
231+
return nil, fmt.Errorf("failed to find subnet %s: %w", subnetName, err)
232+
} else if subnet == nil {
233+
return nil, fmt.Errorf("subnet %s is empty", subnetName)
234+
}
235+
236+
return subnet, nil
237+
}

pkg/infrastructure/gcp/clusterapi/clusterapi.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ func (p Provider) PreProvision(ctx context.Context, in clusterapi.PreProvisionIn
5151
// ServiceAccount for masters
5252
// Only create ServiceAccount for masters if a shared VPC install is not being done
5353
if len(in.InstallConfig.Config.Platform.GCP.NetworkProjectID) == 0 ||
54-
in.InstallConfig.Config.ControlPlane.Platform.GCP.ServiceAccount == "" {
54+
(in.InstallConfig.Config.ControlPlane != nil &&
55+
in.InstallConfig.Config.ControlPlane.Platform.GCP != nil &&
56+
in.InstallConfig.Config.ControlPlane.Platform.GCP.ServiceAccount == "") {
5557
masterSA, err := CreateServiceAccount(ctx, in.InfraID, projectID, "master")
5658
if err != nil {
5759
return fmt.Errorf("failed to create master serviceAccount: %w", err)
@@ -230,7 +232,11 @@ func (p Provider) DestroyBootstrap(ctx context.Context, in clusterapi.BootstrapD
230232
return fmt.Errorf("failed to destroy storage: %w", err)
231233
}
232234

233-
if err := removeBootstrapFirewallRules(ctx, in.Metadata.InfraID, in.Metadata.GCP.ProjectID); err != nil {
235+
projectID := in.Metadata.GCP.ProjectID
236+
if in.Metadata.GCP.NetworkProjectID != "" {
237+
projectID = in.Metadata.GCP.NetworkProjectID
238+
}
239+
if err := removeBootstrapFirewallRules(ctx, in.Metadata.InfraID, projectID); err != nil {
234240
return fmt.Errorf("failed to remove bootstrap firewall rules: %w", err)
235241
}
236242

pkg/infrastructure/gcp/clusterapi/firewallrules.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,30 @@ import (
1111
"github.com/openshift/installer/pkg/types"
1212
)
1313

14+
func getEtcdPorts() []*compute.FirewallAllowed {
15+
return []*compute.FirewallAllowed{
16+
{
17+
IPProtocol: "tcp",
18+
Ports: []string{
19+
"2379-2380",
20+
},
21+
},
22+
}
23+
}
24+
25+
func getHealthChecksPorts() []*compute.FirewallAllowed {
26+
return []*compute.FirewallAllowed{
27+
{
28+
IPProtocol: "tcp",
29+
Ports: []string{
30+
"6080",
31+
"6443",
32+
"22624",
33+
},
34+
},
35+
}
36+
}
37+
1438
func getControlPlanePorts() []*compute.FirewallAllowed {
1539
return []*compute.FirewallAllowed{
1640
{
@@ -186,6 +210,9 @@ func deleteFirewallRule(ctx context.Context, name, projectID string) error {
186210
// createFirewallRules creates the rules needed between the worker and master nodes.
187211
func createFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, network string) error {
188212
projectID := in.InstallConfig.Config.Platform.GCP.ProjectID
213+
if in.InstallConfig.Config.Platform.GCP.NetworkProjectID != "" {
214+
projectID = in.InstallConfig.Config.Platform.GCP.NetworkProjectID
215+
}
189216
workerTag := fmt.Sprintf("%s-worker", in.InfraID)
190217
masterTag := fmt.Sprintf("%s-control-plane", in.InfraID)
191218

@@ -198,6 +225,29 @@ func createFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, net
198225
return err
199226
}
200227

228+
// etcd are needed for master communication for etcd nodes
229+
firewallName = fmt.Sprintf("%s-etcd", in.InfraID)
230+
srcTags = []string{masterTag}
231+
targetTags = []string{masterTag}
232+
srcRanges = []string{}
233+
if err := addFirewallRule(ctx, firewallName, network, projectID, getEtcdPorts(), srcTags, targetTags, srcRanges); err != nil {
234+
return err
235+
}
236+
237+
// Add a single firewall rule to allow the Google Cloud Engine health checks to access all of the services.
238+
// This rule enables the ingress load balancers to determine the health status of their instances.
239+
firewallName = fmt.Sprintf("%s-health-checks", in.InfraID)
240+
srcTags = []string{}
241+
targetTags = []string{masterTag}
242+
srcRanges = []string{"35.191.0.0/16", "130.211.0.0/22"}
243+
if in.InstallConfig.Config.Publish == types.ExternalPublishingStrategy {
244+
// public installs require additional google ip addresses for health checks
245+
srcRanges = append(srcRanges, []string{"209.85.152.0/22", "209.85.204.0/22"}...)
246+
}
247+
if err := addFirewallRule(ctx, firewallName, network, projectID, getHealthChecksPorts(), srcTags, targetTags, srcRanges); err != nil {
248+
return err
249+
}
250+
201251
// internal-cluster rules are needed for worker<->master communication for k8s nodes
202252
firewallName = fmt.Sprintf("%s-internal-cluster", in.InfraID)
203253
srcTags = []string{workerTag, masterTag}
@@ -230,6 +280,9 @@ func createFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, net
230280
// createBootstrapFirewallRules creates the rules needed for the bootstrap node.
231281
func createBootstrapFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, network string) error {
232282
projectID := in.InstallConfig.Config.Platform.GCP.ProjectID
283+
if in.InstallConfig.Config.Platform.GCP.NetworkProjectID != "" {
284+
projectID = in.InstallConfig.Config.Platform.GCP.NetworkProjectID
285+
}
233286
firewallName := fmt.Sprintf("%s-bootstrap-in-ssh", in.InfraID)
234287
srcTags := []string{}
235288
bootstrapTag := fmt.Sprintf("%s-control-plane", in.InfraID)

pkg/infrastructure/gcp/clusterapi/iam.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ func setPolicy(ctx context.Context, crmService *resourcemanager.Service, project
148148

149149
// addMemberToRole adds a member to a role binding.
150150
func addMemberToRole(policy *resourcemanager.Policy, role, member string) error {
151+
var policyBinding *resourcemanager.Binding
152+
151153
for _, binding := range policy.Bindings {
152154
if binding.Role == role {
153155
for _, m := range binding.Members {
@@ -156,11 +158,20 @@ func addMemberToRole(policy *resourcemanager.Policy, role, member string) error
156158
return nil
157159
}
158160
}
159-
binding.Members = append(binding.Members, member)
160-
logrus.Debugf("found %s role, added %s member", role, member)
161-
return nil
161+
policyBinding = binding
162162
}
163163
}
164164

165-
return fmt.Errorf("role %s not found, member %s not added", role, member)
165+
if policyBinding == nil {
166+
policyBinding = &resourcemanager.Binding{
167+
Role: role,
168+
Members: []string{member},
169+
}
170+
logrus.Debugf("creating new policy binding for %s role and %s member", role, member)
171+
policy.Bindings = append(policy.Bindings, policyBinding)
172+
}
173+
174+
policyBinding.Members = append(policyBinding.Members, member)
175+
logrus.Debugf("adding %s role, added %s member", role, member)
176+
return nil
166177
}

0 commit comments

Comments
 (0)