Skip to content

Commit

Permalink
clusterctl: Get API Server endpoint from AWS (openshift#207)
Browse files Browse the repository at this point in the history
* clusterctl: Get API Server endpoint from AWS

* docs: Reminder about the environment
  • Loading branch information
randomvariable authored and k8s-ci-robot committed Oct 9, 2018
1 parent 7109f81 commit 3710984
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 125 deletions.
86 changes: 81 additions & 5 deletions cloud/aws/actuators/cluster/actuator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
package cluster

import (
providerconfigv1 "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/providerconfig/v1alpha1"
service "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/certificates"
ec2svc "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/ec2"
elbsvc "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/elb"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd"
providerconfigv1 "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/providerconfig/v1alpha1"
service "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/certificates"
ec2svc "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/ec2"
elbsvc "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/elb"
clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
client "sigs.k8s.io/cluster-api/pkg/client/clientset_generated/clientset/typed/cluster/v1alpha1"
controllerError "sigs.k8s.io/cluster-api/pkg/controller/error"
Expand Down Expand Up @@ -182,6 +184,80 @@ func (a *Actuator) Delete(cluster *clusterv1.Cluster) error {
return nil
}

// GetIP returns the IP of a machine, but this is going away.
func (a *Actuator) GetIP(cluster *clusterv1.Cluster, machine *clusterv1.Machine) (string, error) {

// Load provider status.
status, err := a.loadProviderStatus(cluster)
if err != nil {
return "", errors.Errorf("failed to load cluster provider status: %v", err)
}

if status.Network.APIServerELB.DNSName != "" {
return status.Network.APIServerELB.DNSName, nil
}

// Load provider config.
config, err := a.loadProviderConfig(cluster)
if err != nil {
return "", errors.Errorf("failed to load cluster provider config: %v", err)
}

sess := a.servicesGetter.Session(config)
elb := a.servicesGetter.ELB(sess)

dnsName, err := elb.GetAPIServerDNSName(cluster.Name)

if err != nil {
return "", errors.Errorf("failed to get DNS name for load balancer")
}

return dnsName, nil
}

// GetKubeConfig returns the kubeconfig after the bootstrap process is complete.
func (a *Actuator) GetKubeConfig(cluster *clusterv1.Cluster, machine *clusterv1.Machine) (string, error) {

// Load provider status.
status, err := a.loadProviderStatus(cluster)
if err != nil {
return "", errors.Errorf("failed to load cluster provider status: %v", err)
}

cert, err := certificates.DecodeCertPEM(status.CACertificate)
if err != nil {
return "", errors.Wrap(err, "failed to decode CA Cert")
}
if cert == nil {
return "", errors.New("certificate not found in status")
}
key, err := certificates.DecodePrivateKeyPEM(status.CAPrivateKey)
if err != nil {
return "", errors.Wrap(err, "failed to decode private key")
}
if key == nil {
return "", errors.New("key not found in status")
}

dnsName, err := a.GetIP(cluster, machine)

if err != nil {
return "", errors.Wrap(err, "failed to get DNS address")
}

server := fmt.Sprintf("https://%s:6443", dnsName)

cfg, err := certificates.NewKubeconfig(server, cert, key)
if err != nil {
return "", errors.Wrap(err, "failed to generate a kubeconfig")
}
yaml, err := clientcmd.Write(*cfg)
if err != nil {
return "", errors.Wrap(err, "failed to serialize config to yaml")
}
return string(yaml), nil
}

func (a *Actuator) loadProviderConfig(cluster *clusterv1.Cluster) (*providerconfigv1.AWSClusterProviderConfig, error) {
providerConfig := &providerconfigv1.AWSClusterProviderConfig{}
err := a.codec.DecodeFromProviderConfig(cluster.Spec.ProviderConfig, providerConfig)
Expand Down
60 changes: 60 additions & 0 deletions cloud/aws/actuators/cluster/actuator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,66 @@ func (d *testServicesGetter) ELB(session *session.Session) service.ELBInterface
return d.elb
}

func TestGetIP(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

c, err := providerconfigv1.NewCodec()
if err != nil {
t.Fatalf("failed to create codec: %v", err)
}

clusters := &clusterGetter{
ci: mock_clusteriface.NewMockClusterInterface(mockCtrl),
}

services := &testServicesGetter{
ec2: mocks.NewMockEC2Interface(mockCtrl),
elb: mocks.NewMockELBInterface(mockCtrl),
}

ap := cluster.ActuatorParams{
Codec: c,
ClustersGetter: clusters,
ServicesGetter: services,
}

actuator, err := cluster.NewActuator(ap)
if err != nil {
t.Fatalf("could not create an actuator: %v", err)
}

cluster := &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{Name: "test", ClusterName: "test"},
Spec: clusterv1.ClusterSpec{
ProviderConfig: clusterv1.ProviderConfig{
Value: &runtime.RawExtension{
Raw: []byte(`{"kind":"AWSClusterProviderConfig","apiVersion":"awsproviderconfig/v1alpha1","region":"us-east-1"}`),
},
},
},
}

machine := &clusterv1.Machine{
ObjectMeta: metav1.ObjectMeta{Name: "test", ClusterName: "test"},
Spec: clusterv1.MachineSpec{
ProviderConfig: clusterv1.ProviderConfig{
Value: &runtime.RawExtension{
Raw: []byte(`{"kind":"AWSClusterProviderConfig","apiVersion":"awsproviderconfig/v1alpha1","region":"us-east-1"}`),
},
},
},
}

services.elb.EXPECT().
GetAPIServerDNSName("test").
Return("", nil)

if _, err := actuator.GetIP(cluster, machine); err != nil {
t.Fatalf("failed to get API server address: %v", err)
}
}

func TestReconcile(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
Expand Down
67 changes: 10 additions & 57 deletions cloud/aws/deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,81 +14,34 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package deployer
package cluster

import (
"fmt"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd"
clustercommon "sigs.k8s.io/cluster-api/pkg/apis/cluster/common"
clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
"github.com/golang/glog"

clusteractuator "sigs.k8s.io/cluster-api-provider-aws/cloud/aws/actuators/cluster"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/providerconfig/v1alpha1"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/certificates"
clustercommon "sigs.k8s.io/cluster-api/pkg/apis/cluster/common"
)

// ProviderName is the name of the cloud provider
const ProviderName = "aws"

func init() {
clustercommon.RegisterClusterProvisioner(ProviderName, &AWSDeployer{})
}

// AWSDeployer implements the cluster-api Deployer interface.
type AWSDeployer struct{}

// GetIP returns the IP of a machine, but this is going away.
func (*AWSDeployer) GetIP(cluster *clusterv1.Cluster, machine *clusterv1.Machine) (string, error) {
codec, err := v1alpha1.NewCodec()
if err != nil {
return "", errors.Wrap(err, "Failed to create codec in deployer")
glog.Fatalf("Could not create codec: %v", err)
}

status := &v1alpha1.AWSClusterProviderStatus{}
if err := codec.DecodeProviderStatus(cluster.Status.ProviderStatus, status); err != nil {
return "", errors.Wrap(err, "failed to decode cluster provider status in deployer")
}
if status.Network.APIServerELB.DNSName == "" {
return "", errors.New("ELB has no DNS name")
}
return status.Network.APIServerELB.DNSName, nil
}

// GetKubeConfig returns the kubeconfig after the bootstrap process is complete.
func (*AWSDeployer) GetKubeConfig(cluster *clusterv1.Cluster, machine *clusterv1.Machine) (string, error) {
codec, err := v1alpha1.NewCodec()
if err != nil {
return "", errors.Wrap(err, "Failed to create codec in deployer")
params := clusteractuator.ActuatorParams{
Codec: codec,
}

status := &v1alpha1.AWSClusterProviderStatus{}
if err := codec.DecodeProviderStatus(cluster.Status.ProviderStatus, status); err != nil {
return "", errors.Wrap(err, "failed to decode machine provider status in deployer")
}
cert, err := certificates.DecodeCertPEM(status.CACertificate)
if err != nil {
return "", errors.Wrap(err, "failed to decode CA Cert")
}
if cert == nil {
return "", errors.New("certificate not found in status")
}
key, err := certificates.DecodePrivateKeyPEM(status.CAPrivateKey)
actuator, err := clusteractuator.NewActuator(params)
if err != nil {
return "", errors.Wrap(err, "failed to decode private key")
}
if key == nil {
return "", errors.New("key not found in status")
glog.Fatalf("Could not create aws cluster actuator: %v", err)
}

server := fmt.Sprintf("https://%s:6443", status.Network.APIServerELB.DNSName)
clustercommon.RegisterClusterProvisioner(ProviderName, actuator)

cfg, err := certificates.NewKubeconfig(server, cert, key)
if err != nil {
return "", errors.Wrap(err, "failed to generate a kubeconfig")
}
yaml, err := clientcmd.Write(*cfg)
if err != nil {
return "", errors.Wrap(err, "failed to serialize config to yaml")
}
return string(yaml), nil
}
61 changes: 0 additions & 61 deletions cloud/aws/deployer/deployer_test.go

This file was deleted.

16 changes: 14 additions & 2 deletions cloud/aws/services/elb/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ package elb

import (
"fmt"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/awserrors"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/wait"
"strings"
"time"

"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/awserrors"
"sigs.k8s.io/cluster-api-provider-aws/cloud/aws/services/wait"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/golang/glog"
Expand Down Expand Up @@ -55,6 +56,17 @@ func (s *Service) ReconcileLoadbalancers(clusterName string, network *v1alpha1.N
return nil
}

// GetAPIServerDNSName returns the DNS name endpoint for the API server
func (s *Service) GetAPIServerDNSName(clusterName string) (string, error) {
apiELB, err := s.describeClassicELB(GenerateELBName(clusterName, TagValueAPIServerRole))

if err != nil {
return "", err
}

return apiELB.DNSName, nil
}

// DeleteLoadbalancers deletes the load balancers for the given cluster.
func (s *Service) DeleteLoadbalancers(clusterName string, network *v1alpha1.Network) error {
glog.V(2).Info("Delete load balancers")
Expand Down
1 change: 1 addition & 0 deletions cloud/aws/services/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ type ELBInterface interface {
ReconcileLoadbalancers(clusterName string, network *providerv1.Network) error
DeleteLoadbalancers(clusterName string, network *providerv1.Network) error
RegisterInstanceWithAPIServerELB(clusterName string, instanceID string) error
GetAPIServerDNSName(clusterName string) (string, error)
}
13 changes: 13 additions & 0 deletions cloud/aws/services/mocks/elb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ sh -c "cd ./clusterctl/examples/aws && ./generate-yaml.sh"
### Starting Cluster API
If you haven't already, set up your [environment]((#setting-up-the-environment))
in the [terminal session] you're working in.
You can now start the Cluster API controllers and deploy a new cluster in AWS:
*Bash:*
Expand Down

0 comments on commit 3710984

Please sign in to comment.