Skip to content

Commit

Permalink
Prefer glibc based Bicep when both musl and glibc are installed (#2722)
Browse files Browse the repository at this point in the history
This matches the behavior of the `az` CLI, which uses this same
logic. When we orginally implemented this, `az` did not have this
special case, it was added later.

Fixes #2683
  • Loading branch information
ellismg committed Sep 8, 2023
1 parent eee777a commit 3f3509a
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 3 deletions.
1 change: 1 addition & 0 deletions cli/azd/.vscode/cspell-azd-dictionary.txt
Expand Up @@ -94,6 +94,7 @@ keychain
LASTEXITCODE
ldflags
lechnerc77
libc
mgmt
mgutz
microsoftonline
Expand Down
23 changes: 20 additions & 3 deletions cli/azd/pkg/tools/bicep/bicep.go
Expand Up @@ -165,11 +165,11 @@ func downloadBicep(ctx context.Context, transporter policy.Transporter, bicepVer
case "darwin":
releaseName = fmt.Sprintf("bicep-osx-%s", arch)
case "linux":
if _, err := os.Stat("/lib/ld-musl-x86_64.so.1"); err == nil {
// As of 0.14.46, there is no version of for AM64 on musl based systems.
if arch == "arm64" {
if preferMuslBicep(os.Stat) {
if runtime.GOARCH != "arm64" {
return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
}

releaseName = "bicep-linux-musl-x64"
} else {
releaseName = fmt.Sprintf("bicep-linux-%s", arch)
Expand Down Expand Up @@ -228,6 +228,23 @@ func downloadBicep(ctx context.Context, transporter policy.Transporter, bicepVer
return nil
}

type stater func(name string) (os.FileInfo, error)

// preferMuslBicep determines if we should install the version of bicep that used musl instead of glibc. We prefer
// musl bicep on linux systems that have musl installed and do not have glibc installed. If both musl and glibc are
// installed, we prefer the glibc based version. This behavior matches the `az` CLI (see: Azure/azure-cli#23040)
func preferMuslBicep(stat stater) bool {
if _, err := stat("/lib/ld-musl-x86_64.so.1"); err == nil {
if _, err := stat("/lib/x86_64-linux-gnu/libc.so.6"); err == nil {
return false
}

return true
}

return false
}

func (cli *bicepCli) version(ctx context.Context) (semver.Version, error) {
bicepRes, err := cli.runCommand(ctx, nil, "--version")
if err != nil {
Expand Down
74 changes: 74 additions & 0 deletions cli/azd/pkg/tools/bicep/bicep_test.go
Expand Up @@ -10,12 +10,14 @@ import (
"path/filepath"
"strings"
"testing"
"time"

"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/test/mocks"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockinput"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -137,3 +139,75 @@ func TestNewBicepCliWillUpgrade(t *testing.T) {

require.Equal(t, []byte(NEW_FILE_CONTENTS), contents)
}

func Test_preferMuslBicep(t *testing.T) {
tests := []struct {
name string
hasMusl bool
hasGlibc bool
want bool
}{
{
name: "musl preferred",
hasMusl: true,
hasGlibc: false,
want: true,
},
{
name: "glibc preferred",
hasMusl: false,
hasGlibc: true,
want: false,
},
{
name: "both available",
hasMusl: true,
hasGlibc: true,
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockStat := func(name string) (os.FileInfo, error) {
if tt.hasMusl && name == "/lib/ld-musl-x86_64.so.1" {
return &fakeFileInfo{}, nil
}
if tt.hasGlibc && name == "/lib/x86_64-linux-gnu/libc.so.6" {
return &fakeFileInfo{}, nil
}

return nil, os.ErrNotExist
}
got := preferMuslBicep(mockStat)
assert.Equal(t, tt.want, got)
})
}
}

type fakeFileInfo struct {
}

func (f *fakeFileInfo) Name() string {
return ""
}

func (f *fakeFileInfo) Size() int64 {
return 0
}

func (f *fakeFileInfo) Mode() os.FileMode {
return 0
}

func (f *fakeFileInfo) ModTime() time.Time {
return time.Time{}
}

func (f *fakeFileInfo) IsDir() bool {
return false
}

func (f *fakeFileInfo) Sys() interface{} {
return nil
}

0 comments on commit 3f3509a

Please sign in to comment.