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

Commit

Permalink
Fix nil linuxprofile (#2621)
Browse files Browse the repository at this point in the history
* check if linuxprofile is nil
  • Loading branch information
zqingqing1 authored and jackfrancis committed Apr 10, 2018
1 parent af09bed commit 7272611
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 66 deletions.
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
}

0 comments on commit 7272611

Please sign in to comment.