cloud/digitalocean/droplet/resources/firewall.go
func (r *Firewall) Apply(actual, expected cloud.Resource, immutable *cluster.Cluster) (*cluster.Cluster, cloud.Resource, error) {
	logger.Debug("firewall.Apply")
	expectedResource := expected.(*Firewall)
	actualResource := actual.(*Firewall)

	isEqual, err := compare.IsEqual(actualResource, expectedResource)
	if err != nil {
		return nil, nil, err
	}
	if isEqual {
		return immutable, expected, nil
	}

	firewallRequest := godo.FirewallRequest{
		Name:          expectedResource.Name,
		InboundRules:  convertInRuleType(expectedResource.InboundRules),
		OutboundRules: convertOutRuleType(expectedResource.OutboundRules),
		DropletIDs:    expectedResource.DropletIDs,
		Tags:          expectedResource.Tags,
	}

	// Make sure Droplets are fully created before applying a firewall
	machineProviderConfigs := immutable.MachineProviderConfigs()
	for _, machineProviderConfig := range machineProviderConfigs {
		for i := 0; i <= TagsGetAttempts; i++ {
			active := true
			droplets, _, err := Sdk.Client.Droplets.ListByTag(context.TODO(), machineProviderConfig.ServerPool.Name, &godo.ListOptions{})
			if err != nil {
				logger.Debug("Hanging for droplets to get created.. (%v)", err)
				time.Sleep(time.Duration(TagsGetTimeout) * time.Second)
				continue
			}
			if len(droplets) == 0 {
				continue
			}
			for _, d := range droplets {
				if d.Status != "active" {
					active = false
					break
				}
			}
			if !active {
				logger.Debug("Waiting for droplets to become active..")
				time.Sleep(time.Duration(TagsGetTimeout) * time.Second)
				continue
			}
			break
		}
	}

	firewall, _, err := Sdk.Client.Firewalls.Create(context.TODO(), &firewallRequest)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create the firewall err: %v", err)
	}
	logger.Success("Created Firewall [%s]", firewall.ID)
	newResource := &Firewall{
		Shared: Shared{
			CloudID: firewall.ID,
			Name:    r.Name,
			Tags:    r.Tags,
		},
		DropletIDs:    r.DropletIDs,
		FirewallID:    firewall.ID,
		InboundRules:  r.InboundRules,
		OutboundRules: r.OutboundRules,
		Created:       r.Created,
	}

	newCluster := r.immutableRender(newResource, immutable)
	return newCluster, newResource, nil
}
func (r *Firewall) Apply(actual, expected cloud.Resource, immutable *cluster.Cluster) (*cluster.Cluster, cloud.Resource, error) {
	logger.Debug("firewall.Apply")
	expectedResource := expected.(*Firewall)
	actualResource := actual.(*Firewall)

	isEqual, err := compare.IsEqual(actualResource, expectedResource)
	if err != nil {
		return nil, nil, err
	}
	if isEqual {
		return immutable, expected, nil
	}

	firewallRequest := godo.FirewallRequest{
		Name:          expectedResource.Name,
		InboundRules:  convertInRuleType(expectedResource.InboundRules),
		OutboundRules: convertOutRuleType(expectedResource.OutboundRules),
		DropletIDs:    expectedResource.DropletIDs,
		Tags:          expectedResource.Tags,
	}

	// Make sure Droplets are fully created before applying a firewall
	machineProviderConfigs := immutable.MachineProviderConfigs()
	for _, machineProviderConfig := range machineProviderConfigs {
		for i := 0; i <= TagsGetAttempts; i++ {
			active := true
			droplets, _, err := Sdk.Client.Droplets.ListByTag(context.TODO(), machineProviderConfig.ServerPool.Name, &godo.ListOptions{})
			if err != nil {
				logger.Debug("Hanging for droplets to get created.. (%v)", err)
				time.Sleep(time.Duration(TagsGetTimeout) * time.Second)
				continue
			}
			if len(droplets) == 0 {
				continue
			}
			for _, d := range droplets {
				if d.Status != "active" {
					active = false
					break
				}
			}
			if !active {
				logger.Debug("Waiting for droplets to become active..")
				time.Sleep(time.Duration(TagsGetTimeout) * time.Second)
				continue
			}
			break
		}
	}

	firewall, _, err := Sdk.Client.Firewalls.Create(context.TODO(), &firewallRequest)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create the firewall err: %v", err)
	}
	logger.Success("Created Firewall [%s]", firewall.ID)
	newResource := &Firewall{
		Shared: Shared{
			CloudID: firewall.ID,
			Name:    r.Name,
			Tags:    r.Tags,
		},
		DropletIDs:    r.DropletIDs,
		FirewallID:    firewall.ID,
		InboundRules:  r.InboundRules,
		OutboundRules: r.OutboundRules,
		Created:       r.Created,
	}

	newCluster := r.immutableRender(newResource, immutable)
	return newCluster, newResource, nil
}
// Apply will compare the actual and expected firewall config, if needed it will create the firewall.