Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Fix nil linuxprofile #2621

Merged
merged 9 commits into from
Apr 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ func (dc *deployCmd) validate(cmd *cobra.Command, args []string) error {
func autofillApimodel(dc *deployCmd) {
var err error

if dc.containerService.Properties.LinuxProfile.AdminUsername == "" {
log.Warnf("apimodel: no linuxProfile.adminUsername was specified. Will use 'azureuser'.")
dc.containerService.Properties.LinuxProfile.AdminUsername = "azureuser"
if dc.containerService.Properties.LinuxProfile != nil {
if dc.containerService.Properties.LinuxProfile.AdminUsername == "" {
log.Warnf("apimodel: no linuxProfile.adminUsername was specified. Will use 'azureuser'.")
dc.containerService.Properties.LinuxProfile.AdminUsername = "azureuser"
}
}

if dc.dnsPrefix != "" && dc.containerService.Properties.MasterProfile.DNSPrefix != "" {
Expand Down Expand Up @@ -181,15 +183,13 @@ func autofillApimodel(dc *deployCmd) {
}
}

if dc.containerService.Properties.LinuxProfile.SSH.PublicKeys == nil ||
if dc.containerService.Properties.LinuxProfile != nil && (dc.containerService.Properties.LinuxProfile.SSH.PublicKeys == nil ||
len(dc.containerService.Properties.LinuxProfile.SSH.PublicKeys) == 0 ||
dc.containerService.Properties.LinuxProfile.SSH.PublicKeys[0].KeyData == "" {
creator := &acsengine.SSHCreator{
Translator: &i18n.Translator{
Locale: dc.locale,
},
dc.containerService.Properties.LinuxProfile.SSH.PublicKeys[0].KeyData == "") {
translator := &i18n.Translator{
Locale: dc.locale,
}
_, publicKey, err := creator.CreateSaveSSH(dc.containerService.Properties.LinuxProfile.AdminUsername, dc.outputDirectory)
_, publicKey, err := acsengine.CreateSaveSSH(dc.containerService.Properties.LinuxProfile.AdminUsername, dc.outputDirectory, translator)
if err != nil {
log.Fatal("Failed to generate SSH Key")
}
Expand Down
40 changes: 5 additions & 35 deletions pkg/acsengine/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,23 @@ import (
"crypto/rand"
"crypto/rsa"
"fmt"
"io"

"github.com/Azure/acs-engine/pkg/helpers"
"github.com/Azure/acs-engine/pkg/i18n"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)

// SSHCreator represents the object that creates SSH key pair
type SSHCreator struct {
Translator *i18n.Translator
}

const (
// SSHKeySize is the size (in bytes) of SSH key to create
SSHKeySize = 4096
)

// CreateSaveSSH generates and stashes an SSH key pair.
func (s *SSHCreator) CreateSaveSSH(username, outputDirectory string) (privateKey *rsa.PrivateKey, publicKeyString string, err error) {
privateKey, publicKeyString, err = s.CreateSSH(rand.Reader)
func CreateSaveSSH(username, outputDirectory string, s *i18n.Translator) (privateKey *rsa.PrivateKey, publicKeyString string, err error) {

privateKey, publicKeyString, err = helpers.CreateSSH(rand.Reader, s)
if err != nil {
return nil, "", err
}

privateKeyPem := privateKeyToPem(privateKey)

f := &FileSaver{
Translator: s.Translator,
Translator: s,
}

err = f.SaveFile(outputDirectory, fmt.Sprintf("%s_rsa", username), privateKeyPem)
Expand All @@ -41,22 +30,3 @@ func (s *SSHCreator) CreateSaveSSH(username, outputDirectory string) (privateKey

return privateKey, publicKeyString, nil
}

// CreateSSH creates an SSH key pair.
func (s *SSHCreator) CreateSSH(rg io.Reader) (privateKey *rsa.PrivateKey, publicKeyString string, err error) {
log.Debugf("ssh: generating %dbit rsa key", SSHKeySize)
privateKey, err = rsa.GenerateKey(rg, SSHKeySize)
if err != nil {
return nil, "", s.Translator.Errorf("failed to generate private key for ssh: %q", err)
}

publicKey := privateKey.PublicKey
sshPublicKey, err := ssh.NewPublicKey(&publicKey)
if err != nil {
return nil, "", s.Translator.Errorf("failed to create openssh public key string: %q", err)
}
authorizedKeyBytes := ssh.MarshalAuthorizedKey(sshPublicKey)
authorizedKey := string(authorizedKeyBytes)

return privateKey, authorizedKey, nil
}
10 changes: 7 additions & 3 deletions pkg/acsengine/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package acsengine
import (
"math/rand"
"testing"

"github.com/Azure/acs-engine/pkg/helpers"

"github.com/Azure/acs-engine/pkg/i18n"
)

func TestCreateSSH(t *testing.T) {
Expand Down Expand Up @@ -63,11 +67,11 @@ EPDesL0rH+3s1CKpgkhYdbJ675GFoGoq+X21QaqsdvoXmmuJF9qq9Tq+JaWloUNq
-----END RSA PRIVATE KEY-----
`

creator := &SSHCreator{
Translator: nil,
translator := &i18n.Translator{
Locale: nil,
}

privateKey, publicKey, err := creator.CreateSSH(rg)
privateKey, publicKey, err := helpers.CreateSSH(rg, translator)
if err != nil {
t.Fatalf("failed to generate SSH: %s", err)
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/acsengine/testdata/agentPoolOnly/v20180331/agents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"apiVersion": "2018-03-31",
"properties": {
"dnsPrefix": "agents006",
"fqdn": "agents006.azmk8s.io",
"kubernetesVersion": "1.8.7",
"agentPoolProfiles": [
{
"name": "agentpool1",
"count": 1,
"vmSize": "Standard_D2_v2"
}
],
"servicePrincipalProfile": {
"clientID": "ServicePrincipalClientID",
"secret": "myServicePrincipalClientSecret"
}
}
}
7 changes: 5 additions & 2 deletions pkg/api/agentPoolOnlyApi/v20180331/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ func (a *Properties) Validate() error {
}
}

if e := a.LinuxProfile.Validate(); e != nil {
return e
if a.LinuxProfile != nil {
if e := a.LinuxProfile.Validate(); e != nil {
return e
}
}

if e := validateVNET(a); e != nil {
return e
}
Expand Down
46 changes: 30 additions & 16 deletions pkg/api/apiloader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"crypto/rand"
"encoding/json"
"io/ioutil"
"reflect"
Expand Down Expand Up @@ -45,7 +46,7 @@ func (a *Apiloader) DeserializeContainerService(contents []byte, validate, isUpd
if service == nil || err != nil {
if isAgentPoolOnlyClusterJSON(contents) {
log.Info("No masterProfile: interpreting API model as agent pool only")
service, err := a.LoadContainerServiceForAgentPoolOnlyCluster(contents, version, validate, isUpdate, "")
service, _, err := a.LoadContainerServiceForAgentPoolOnlyCluster(contents, version, validate, isUpdate, "")
if service == nil || err != nil {
log.Infof("Error returned by LoadContainerServiceForAgentPoolOnlyCluster: %+v", err)
}
Expand Down Expand Up @@ -185,59 +186,72 @@ func (a *Apiloader) LoadContainerService(
}

// LoadContainerServiceForAgentPoolOnlyCluster loads an ACS Cluster API Model, validates it, and returns the unversioned representation
func (a *Apiloader) LoadContainerServiceForAgentPoolOnlyCluster(contents []byte, version string, validate, isUpdate bool, defaultKubernetesVersion string) (*ContainerService, error) {
func (a *Apiloader) LoadContainerServiceForAgentPoolOnlyCluster(contents []byte, version string, validate, isUpdate bool, defaultKubernetesVersion string) (*ContainerService, bool, error) {
IsSSHAutoGenerated := false
switch version {
case v20170831.APIVersion:
managedCluster := &v20170831.ManagedCluster{}
if e := json.Unmarshal(contents, &managedCluster); e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
// verify orchestrator version
if len(managedCluster.Properties.KubernetesVersion) > 0 && !common.AllKubernetesSupportedVersions[managedCluster.Properties.KubernetesVersion] {
return nil, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", managedCluster.Properties.KubernetesVersion)
return nil, IsSSHAutoGenerated, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", managedCluster.Properties.KubernetesVersion)
}
// use defaultKubernetesVersion arg if no version was supplied in the request contents
if managedCluster.Properties.KubernetesVersion == "" && defaultKubernetesVersion != "" {
if !common.AllKubernetesSupportedVersions[defaultKubernetesVersion] {
return nil, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", defaultKubernetesVersion)
return nil, IsSSHAutoGenerated, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", defaultKubernetesVersion)
}
managedCluster.Properties.KubernetesVersion = defaultKubernetesVersion
}
if e := managedCluster.Properties.Validate(); validate && e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
return ConvertV20170831AgentPoolOnly(managedCluster), nil
return ConvertV20170831AgentPoolOnly(managedCluster), false, nil
case v20180331.APIVersion:
managedCluster := &v20180331.ManagedCluster{}
if e := json.Unmarshal(contents, &managedCluster); e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
// verify orchestrator version
if len(managedCluster.Properties.KubernetesVersion) > 0 && !common.AllKubernetesSupportedVersions[managedCluster.Properties.KubernetesVersion] {
return nil, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", managedCluster.Properties.KubernetesVersion)
return nil, IsSSHAutoGenerated, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", managedCluster.Properties.KubernetesVersion)
}
// use defaultKubernetesVersion arg if no version was supplied in the request contents
if managedCluster.Properties.KubernetesVersion == "" && defaultKubernetesVersion != "" {
if !common.AllKubernetesSupportedVersions[defaultKubernetesVersion] {
return nil, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", defaultKubernetesVersion)
return nil, IsSSHAutoGenerated, a.Translator.Errorf("The selected orchestrator version '%s' is not supported", defaultKubernetesVersion)
}
managedCluster.Properties.KubernetesVersion = defaultKubernetesVersion
}
if e := managedCluster.Properties.Validate(); validate && e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
return ConvertV20180331AgentPoolOnly(managedCluster), nil

if managedCluster.Properties.LinuxProfile == nil {
linuxProfile := &v20180331.LinuxProfile{}
linuxProfile.AdminUsername = "azureuser"
_, publicKey, err := helpers.CreateSSH(rand.Reader, a.Translator)
if err != nil {
return nil, IsSSHAutoGenerated, err
}
linuxProfile.SSH.PublicKeys = []v20180331.PublicKey{{KeyData: publicKey}}
managedCluster.Properties.LinuxProfile = linuxProfile
IsSSHAutoGenerated = true
}
return ConvertV20180331AgentPoolOnly(managedCluster), IsSSHAutoGenerated, nil
case apvlabs.APIVersion:
managedCluster := &apvlabs.ManagedCluster{}
if e := json.Unmarshal(contents, &managedCluster); e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
if e := managedCluster.Properties.Validate(); validate && e != nil {
return nil, e
return nil, IsSSHAutoGenerated, e
}
return ConvertVLabsAgentPoolOnly(managedCluster), nil
return ConvertVLabsAgentPoolOnly(managedCluster), IsSSHAutoGenerated, nil
default:
return nil, a.Translator.Errorf("unrecognized APIVersion in LoadContainerServiceForAgentPoolOnlyCluster '%s'", version)
return nil, IsSSHAutoGenerated, a.Translator.Errorf("unrecognized APIVersion in LoadContainerServiceForAgentPoolOnlyCluster '%s'", version)
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/api/convertertoagentpoolonlyapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ func convertV20180331AgentPoolOnlyProperties(obj *v20180331.Properties) *Propert
if obj.LinuxProfile != nil {
properties.LinuxProfile = convertV20180331AgentPoolOnlyLinuxProfile(obj.LinuxProfile)
}

if obj.WindowsProfile != nil {
properties.WindowsProfile = convertV20180331AgentPoolOnlyWindowsProfile(obj.WindowsProfile)
}
Expand Down
28 changes: 28 additions & 0 deletions pkg/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ package helpers
import (
// "fmt"
"bytes"
"crypto/rsa"
"encoding/json"
"io"

"github.com/Azure/acs-engine/pkg/i18n"
"golang.org/x/crypto/ssh"
)

const (
// SSHKeySize is the size (in bytes) of SSH key to create
SSHKeySize = 4096
)

// JSONMarshalIndent marshals formatted JSON w/ optional SetEscapeHTML
Expand Down Expand Up @@ -46,3 +56,21 @@ func PointerToBool(b bool) *bool {
p := b
return &p
}

// CreateSSH creates an SSH key pair.
func CreateSSH(rg io.Reader, s *i18n.Translator) (privateKey *rsa.PrivateKey, publicKeyString string, err error) {
privateKey, err = rsa.GenerateKey(rg, SSHKeySize)
if err != nil {
return nil, "", s.Errorf("failed to generate private key for ssh: %q", err)
}

publicKey := privateKey.PublicKey
sshPublicKey, err := ssh.NewPublicKey(&publicKey)
if err != nil {
return nil, "", s.Errorf("failed to create openssh public key string: %q", err)
}
authorizedKeyBytes := ssh.MarshalAuthorizedKey(sshPublicKey)
authorizedKey := string(authorizedKeyBytes)

return privateKey, authorizedKey, nil
}