github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/os/base_windows.go (about) 1 // Copyright 2024 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package os 5 6 import ( 7 "os" 8 "strings" 9 10 "github.com/juju/errors" 11 "golang.org/x/sys/windows/registry" 12 13 corebase "github.com/juju/juju/core/base" 14 ) 15 16 var ( 17 // currentVersionKey is defined as a variable instead of a constant 18 // to allow overwriting during testing 19 currentVersionKey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" 20 21 // isNanoKey determines the registry key that can be queried to determine whether 22 // a machine is a nano machine 23 isNanoKey = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels" 24 25 // Windows versions come in various flavors: 26 // Standard, Datacenter, etc. We use string prefix match them to one 27 // of the following. Specify the longest name in a particular series first 28 // For example, if we have "Win 2012" and "Win 2012 R2", we specify "Win 2012 R2" first. 29 // We need to make sure we manually update this list with each new windows release. 30 windowsVersionMatchOrder = []string{ 31 "Hyper-V Server 2012 R2", 32 "Hyper-V Server 2012", 33 "Windows Server 2008 R2", 34 "Windows Server 2012 R2", 35 "Windows Server 2012", 36 "Hyper-V Server 2016", 37 "Windows Server 2016", 38 "Windows Server 2019", 39 "Windows Storage Server 2012 R2", 40 "Windows Storage Server 2012", 41 "Windows Storage Server 2016", 42 "Windows 7", 43 "Windows 8.1", 44 "Windows 8", 45 "Windows 10", 46 "Windows 11", 47 } 48 49 // windowsVersions is a mapping consisting of the output from 50 // the following WMI query: (gwmi Win32_OperatingSystem).Name 51 windowsVersions = map[string]string{ 52 "Hyper-V Server 2012 R2": "2012hvr2", 53 "Hyper-V Server 2012": "2012hv", 54 "Windows Server 2008 R2": "2008r2", 55 "Windows Server 2012 R2": "2012r2", 56 "Windows Server 2012": "2012", 57 "Hyper-V Server 2016": "2016hv", 58 "Windows Server 2016": "2016", 59 "Windows Server 2019": "2019", 60 "Windows Storage Server 2012 R2": "2012r2", 61 "Windows Storage Server 2012": "2012", 62 "Windows Storage Server 2016": "2016", 63 "Windows Storage Server 2019": "2019", 64 "Windows 7": "7", 65 "Windows 8.1": "81", 66 "Windows 8": "8", 67 "Windows 10": "10", 68 "Windows 11": "11", 69 } 70 71 // windowsNanoVersions is a mapping from the product name 72 // stored in registry to a juju defined nano-series 73 // On the nano version so far the product name actually 74 // is identical to the correspondent main windows version 75 // and the information about it being nano is stored in 76 // a different place. 77 windowsNanoVersions = map[string]string{ 78 "Windows Server 2016": "2016nano", 79 } 80 ) 81 82 func getVersionFromRegistry() (string, error) { 83 k, err := registry.OpenKey(registry.LOCAL_MACHINE, currentVersionKey, registry.QUERY_VALUE) 84 if err != nil { 85 return "", errors.Trace(err) 86 } 87 defer k.Close() 88 s, _, err := k.GetStringValue("ProductName") 89 if err != nil { 90 return "", errors.Trace(err) 91 } 92 93 return s, nil 94 } 95 96 func readBase() (corebase.Base, error) { 97 ver, err := getVersionFromRegistry() 98 if err != nil { 99 return corebase.Base{}, errors.Trace(err) 100 } 101 102 var lookAt = windowsVersions 103 104 isNano, err := isWindowsNano() 105 if err != nil && os.IsNotExist(err) { 106 return corebase.Base{}, errors.Trace(err) 107 } 108 if isNano { 109 lookAt = windowsNanoVersions 110 } 111 112 for _, value := range windowsVersionMatchOrder { 113 if strings.HasPrefix(ver, value) { 114 if val, ok := lookAt[value]; ok { 115 return corebase.ParseBase("win", val) 116 } 117 } 118 } 119 return corebase.Base{}, errors.Errorf("unknown series %q", ver) 120 } 121 122 func isWindowsNano() (bool, error) { 123 k, err := registry.OpenKey(registry.LOCAL_MACHINE, isNanoKey, registry.QUERY_VALUE) 124 if err != nil { 125 return false, errors.Trace(err) 126 } 127 defer k.Close() 128 129 s, _, err := k.GetIntegerValue("NanoServer") 130 if err != nil { 131 return false, errors.Trace(err) 132 } 133 return s == 1, nil 134 }