Adding New Default Image Config Value to IC. (#2434)

* Adding New Default Image Config Value to IC.

* Removing forced image setting.

* Updating Webhook Events.

* Removing typo.

* Updating webhook_events again.

* Syncing webhook events.

* Fixing check for os type.

* Fixing import.

* PR Suggestions.

* Fix C# Model Typo.

* Removing other refs to images.

* Removing remaining refs to images outside of models.

* Removing hardcoded image values from tests.

* Update Default Proxy and Repro Images.

Co-authored-by: Marc Greisen <mgreisen@microsoft.com>
This commit is contained in:
Noah McGregor Harper
2022-09-23 10:40:44 -07:00
committed by GitHub
parent dc2c4649c8
commit 3f35d81f4b
15 changed files with 78 additions and 52 deletions

View File

@ -682,6 +682,8 @@ If webhook is set to have Event Grid message format then the payload will look a
"allowed_aad_tenants": [ "allowed_aad_tenants": [
"00000000-0000-0000-0000-000000000000" "00000000-0000-0000-0000-000000000000"
], ],
"default_linux_vm_image": "Canonical:UbuntuServer:18.04-LTS:latest",
"default_windows_vm_image": "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest",
"network_config": { "network_config": {
"address_space": "10.0.0.0/8", "address_space": "10.0.0.0/8",
"subnet": "10.0.0.0/16" "subnet": "10.0.0.0/16"
@ -822,6 +824,16 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Api Access Rules", "title": "Api Access Rules",
"type": "object" "type": "object"
}, },
"default_linux_vm_image": {
"default": "Canonical:UbuntuServer:18.04-LTS:latest",
"title": "Default Linux Vm Image",
"type": "string"
},
"default_windows_vm_image": {
"default": "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest",
"title": "Default Windows Vm Image",
"type": "string"
},
"extensions": { "extensions": {
"$ref": "#/definitions/AzureVmExtensionConfig" "$ref": "#/definitions/AzureVmExtensionConfig"
}, },
@ -6046,6 +6058,16 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Api Access Rules", "title": "Api Access Rules",
"type": "object" "type": "object"
}, },
"default_linux_vm_image": {
"default": "Canonical:UbuntuServer:18.04-LTS:latest",
"title": "Default Linux Vm Image",
"type": "string"
},
"default_windows_vm_image": {
"default": "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest",
"title": "Default Windows Vm Image",
"type": "string"
},
"extensions": { "extensions": {
"$ref": "#/definitions/AzureVmExtensionConfig" "$ref": "#/definitions/AzureVmExtensionConfig"
}, },

View File

@ -75,6 +75,18 @@ public class Scaleset {
context: "ScalesetCreate"); context: "ScalesetCreate");
} }
string image;
if (create.Image is null) {
var config = await _context.ConfigOperations.Fetch();
if (pool.Os == Os.Windows) {
image = config.DefaultWindowsVmImage;
} else {
image = config.DefaultLinuxVmImage;
}
} else {
image = create.Image;
}
Region region; Region region;
if (create.Region is null) { if (create.Region is null) {
region = await _context.Creds.GetBaseRegion(); region = await _context.Creds.GetBaseRegion();
@ -117,7 +129,7 @@ public class Scaleset {
Auth: await Auth.BuildAuth(_log), Auth: await Auth.BuildAuth(_log),
PoolName: create.PoolName, PoolName: create.PoolName,
VmSku: create.VmSku, VmSku: create.VmSku,
Image: create.Image, Image: image,
Region: region, Region: region,
Size: create.Size, Size: create.Size,
SpotInstances: create.SpotInstances, SpotInstances: create.SpotInstances,

View File

@ -329,6 +329,8 @@ public record InstanceConfig
[DefaultValue(InitMethod.DefaultConstructor)] NetworkConfig NetworkConfig, [DefaultValue(InitMethod.DefaultConstructor)] NetworkConfig NetworkConfig,
[DefaultValue(InitMethod.DefaultConstructor)] NetworkSecurityGroupConfig ProxyNsgConfig, [DefaultValue(InitMethod.DefaultConstructor)] NetworkSecurityGroupConfig ProxyNsgConfig,
AzureVmExtensionConfig? Extensions, AzureVmExtensionConfig? Extensions,
string DefaultWindowsVmImage = "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest",
string DefaultLinuxVmImage = "Canonical:UbuntuServer:18.04-LTS:latest",
string ProxyVmSku = "Standard_B2s", string ProxyVmSku = "Standard_B2s",
bool RequireAdminPrivileges = false, bool RequireAdminPrivileges = false,
IDictionary<Endpoint, ApiAccessRule>? ApiAccessRules = null, IDictionary<Endpoint, ApiAccessRule>? ApiAccessRules = null,
@ -343,6 +345,8 @@ public record InstanceConfig
new NetworkConfig(), new NetworkConfig(),
new NetworkSecurityGroupConfig(), new NetworkSecurityGroupConfig(),
null, null,
"MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest",
"Canonical:UbuntuServer:18.04-LTS:latest",
"Standard_B2s", "Standard_B2s",
false false
) { } ) { }

View File

@ -190,7 +190,7 @@ public record ProxyReset(
public record ScalesetCreate( public record ScalesetCreate(
[property: Required] PoolName PoolName, [property: Required] PoolName PoolName,
[property: Required] string VmSku, [property: Required] string VmSku,
[property: Required] string Image, string? Image,
Region? Region, Region? Region,
[property: Range(1, long.MaxValue), Required] long Size, [property: Range(1, long.MaxValue), Required] long Size,
[property: Required] bool SpotInstances, [property: Required] bool SpotInstances,

View File

@ -236,7 +236,7 @@ public class ProxyOperations : StatefulOrm<Proxy, VmState, ProxyOperations>, IPr
public static Vm GetVm(Proxy proxy, InstanceConfig config) { public static Vm GetVm(Proxy proxy, InstanceConfig config) {
var tags = config.VmssTags; var tags = config.VmssTags;
string proxyVmSku; string proxyVmSku;
const string PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest"; string proxyImage = config.DefaultLinuxVmImage;
if (config.ProxyVmSku is null) { if (config.ProxyVmSku is null) {
proxyVmSku = "Standard_B2s"; proxyVmSku = "Standard_B2s";
} else { } else {
@ -247,7 +247,7 @@ public class ProxyOperations : StatefulOrm<Proxy, VmState, ProxyOperations>, IPr
Name: $"proxy-{proxy.ProxyId:N}", Name: $"proxy-{proxy.ProxyId:N}",
Region: proxy.Region, Region: proxy.Region,
Sku: proxyVmSku, Sku: proxyVmSku,
Image: PROXY_IMAGE, Image: proxyImage,
Auth: proxy.Auth, Auth: proxy.Auth,
Tags: tags, Tags: tags,
Nsg: null Nsg: null

View File

@ -30,16 +30,9 @@ public interface IReproOperations : IStatefulOrm<Repro, VmState> {
} }
public class ReproOperations : StatefulOrm<Repro, VmState, ReproOperations>, IReproOperations { public class ReproOperations : StatefulOrm<Repro, VmState, ReproOperations>, IReproOperations {
private static readonly Dictionary<Os, string> DEFAULT_OS = new()
{
{ Os.Linux, "Canonical:UbuntuServer:18.04-LTS:latest" },
{ Os.Windows, "MicrosoftWindowsDesktop:Windows-10:20h2-pro:latest" }
};
const string DEFAULT_SKU = "Standard_DS1_v2"; const string DEFAULT_SKU = "Standard_DS1_v2";
public ReproOperations(ILogTracer log, IOnefuzzContext context) public ReproOperations(ILogTracer log, IOnefuzzContext context)
: base(log, context) { : base(log, context) {
@ -57,16 +50,22 @@ public class ReproOperations : StatefulOrm<Repro, VmState, ReproOperations>, IRe
throw new Exception($"previous existing task missing: {repro.TaskId}"); throw new Exception($"previous existing task missing: {repro.TaskId}");
} }
Dictionary<Os, string> default_os = new()
{
{ Os.Linux, config.DefaultLinuxVmImage },
{ Os.Windows, config.DefaultWindowsVmImage }
};
var vmConfig = await taskOperations.GetReproVmConfig(task); var vmConfig = await taskOperations.GetReproVmConfig(task);
if (vmConfig == null) { if (vmConfig == null) {
if (!DEFAULT_OS.ContainsKey(task.Os)) { if (!default_os.ContainsKey(task.Os)) {
throw new NotSupportedException($"unsupport OS for repro {task.Os}"); throw new NotSupportedException($"unsupport OS for repro {task.Os}");
} }
vmConfig = new TaskVm( vmConfig = new TaskVm(
await _context.Creds.GetBaseRegion(), await _context.Creds.GetBaseRegion(),
DEFAULT_SKU, DEFAULT_SKU,
DEFAULT_OS[task.Os], default_os[task.Os],
null null
); );
} }

View File

@ -57,9 +57,6 @@ public class Scaleset : IFromJsonElement<Scaleset> {
public class ScalesetApi : ApiBase { public class ScalesetApi : ApiBase {
public const string Image_Ubuntu_20_04 = "Canonical:0001-com-ubuntu-server-focal:20_04-lts:latest";
public const string ImageWindows = "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest";
public ScalesetApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) : public ScalesetApi(Uri endpoint, Microsoft.OneFuzz.Service.Request request, ITestOutputHelper output) :
base(endpoint, "/api/Scaleset", request, output) { } base(endpoint, "/api/Scaleset", request, output) { }
@ -73,7 +70,7 @@ public class ScalesetApi : ApiBase {
return IEnumerableResult<Scaleset>(res); return IEnumerableResult<Scaleset>(res);
} }
public async Task<Result<Scaleset, Error>> Create(string poolName, int size, string? region = null, string vmSku = "Standard_D2s_v3", string image = Image_Ubuntu_20_04, bool spotInstance = false) { public async Task<Result<Scaleset, Error>> Create(string poolName, int size, string? region = null, string vmSku = "Standard_D2s_v3", string? image = null, bool spotInstance = false) {
_output.WriteLine($"Creating scaleset in pool {poolName}, size: {size}"); _output.WriteLine($"Creating scaleset in pool {poolName}, size: {size}");
var rootScalesetCreate = new JsonObject() var rootScalesetCreate = new JsonObject()

View File

@ -3,14 +3,13 @@
namespace FunctionalTests { namespace FunctionalTests {
public class Helpers { public class Helpers {
public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os = "linux", string? region = null, int numNodes = 2) { public static async Task<(Pool, Scaleset)> CreatePoolAndScaleset(PoolApi poolApi, ScalesetApi scalesetApi, string os = "linux", string? region = null, int numNodes = 2) {
var image = (os == "linux") ? ScalesetApi.Image_Ubuntu_20_04 : ScalesetApi.ImageWindows;
var newPoolId = Guid.NewGuid().ToString(); var newPoolId = Guid.NewGuid().ToString();
var newPoolName = PoolApi.TestPoolPrefix + newPoolId; var newPoolName = PoolApi.TestPoolPrefix + newPoolId;
var newPool = await poolApi.Create(newPoolName, os); var newPool = await poolApi.Create(newPoolName, os);
Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}"); Assert.True(newPool.IsOk, $"failed to create new pool: {newPool.ErrorV}");
var newScalesetResult = await scalesetApi.Create(newPool.OkV!.Name, numNodes, region: region, image: image); var newScalesetResult = await scalesetApi.Create(newPool.OkV!.Name, numNodes, region: region);
Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}"); Assert.True(newScalesetResult.IsOk, $"failed to crate new scaleset: {newScalesetResult.ErrorV}");
var newScaleset = newScalesetResult.OkV!; var newScaleset = newScalesetResult.OkV!;

View File

@ -43,7 +43,6 @@ from .extension import proxy_manager_extensions
from .orm import ORMMixin, QueryFilter from .orm import ORMMixin, QueryFilter
from .proxy_forward import ProxyForward from .proxy_forward import ProxyForward
PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest"
PROXY_LOG_PREFIX = "scaleset-proxy: " PROXY_LOG_PREFIX = "scaleset-proxy: "
PROXY_LIFESPAN = datetime.timedelta(days=7) PROXY_LIFESPAN = datetime.timedelta(days=7)
@ -70,6 +69,7 @@ class Proxy(ORMMixin):
return ("region", "proxy_id") return ("region", "proxy_id")
def get_vm(self, config: InstanceConfig) -> VM: def get_vm(self, config: InstanceConfig) -> VM:
config = InstanceConfig.fetch()
sku = config.proxy_vm_sku sku = config.proxy_vm_sku
tags = None tags = None
if config.vm_tags: if config.vm_tags:
@ -78,7 +78,7 @@ class Proxy(ORMMixin):
name="proxy-%s" % base58.b58encode(self.proxy_id.bytes).decode(), name="proxy-%s" % base58.b58encode(self.proxy_id.bytes).decode(),
region=self.region, region=self.region,
sku=sku, sku=sku,
image=PROXY_IMAGE, image=config.default_linux_vm_image,
auth=self.auth, auth=self.auth,
tags=tags, tags=tags,
) )

View File

@ -27,11 +27,6 @@ from .orm import ORMMixin, QueryFilter
from .reports import get_report from .reports import get_report
from .tasks.main import Task from .tasks.main import Task
DEFAULT_OS = {
OS.linux: "Canonical:UbuntuServer:18.04-LTS:latest",
OS.windows: "MicrosoftWindowsDesktop:Windows-10:20h2-pro:latest",
}
DEFAULT_SKU = "Standard_DS1_v2" DEFAULT_SKU = "Standard_DS1_v2"
@ -56,14 +51,19 @@ class Repro(BASE_REPRO, ORMMixin):
if isinstance(task, Error): if isinstance(task, Error):
raise Exception("previously existing task missing: %s" % self.task_id) raise Exception("previously existing task missing: %s" % self.task_id)
config = InstanceConfig.fetch()
default_os = {
OS.linux: config.default_linux_vm_image,
OS.windows: config.default_windows_vm_image,
}
vm_config = task.get_repro_vm_config() vm_config = task.get_repro_vm_config()
if vm_config is None: if vm_config is None:
# if using a pool without any scalesets defined yet, use reasonable defaults # if using a pool without any scalesets defined yet, use reasonable defaults
if task.os not in DEFAULT_OS: if task.os not in default_os:
raise NotImplementedError("unsupported OS for repro %s" % task.os) raise NotImplementedError("unsupported OS for repro %s" % task.os)
vm_config = TaskVm( vm_config = TaskVm(
region=get_base_region(), sku=DEFAULT_SKU, image=DEFAULT_OS[task.os] region=get_base_region(), sku=DEFAULT_SKU, image=default_os[task.os]
) )
if self.auth is None: if self.auth is None:

View File

@ -4,7 +4,7 @@
# Licensed under the MIT License. # Licensed under the MIT License.
import azure.functions as func import azure.functions as func
from onefuzztypes.enums import ErrorCode, ScalesetState from onefuzztypes.enums import OS, ErrorCode, ScalesetState
from onefuzztypes.models import Error from onefuzztypes.models import Error
from onefuzztypes.requests import ( from onefuzztypes.requests import (
ScalesetCreate, ScalesetCreate,
@ -78,6 +78,14 @@ def post(req: func.HttpRequest) -> func.HttpResponse:
region = request.region region = request.region
if request.image is None:
if pool.os == OS.windows:
image = instance_config.default_windows_vm_image
else:
image = instance_config.default_linux_vm_image
else:
image = request.image
if request.vm_sku not in list_available_skus(region): if request.vm_sku not in list_available_skus(region):
return not_ok( return not_ok(
Error( Error(
@ -97,7 +105,7 @@ def post(req: func.HttpRequest) -> func.HttpResponse:
scaleset = Scaleset.create( scaleset = Scaleset.create(
pool_name=request.pool_name, pool_name=request.pool_name,
vm_sku=request.vm_sku, vm_sku=request.vm_sku,
image=request.image, image=image,
region=region, region=region,
size=request.size, size=request.size,
spot_instances=request.spot_instances, spot_instances=request.spot_instances,

View File

@ -48,9 +48,6 @@ ONEFUZZ_GUID_NAMESPACE = uuid.UUID("27f25e3f-6544-4b69-b309-9b096c5a9cbc")
ONE_HOUR_IN_SECONDS = 3600 ONE_HOUR_IN_SECONDS = 3600
DEFAULT_LINUX_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest"
DEFAULT_WINDOWS_IMAGE = "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest"
REPRO_SSH_FORWARD = "1337:127.0.0.1:1337" REPRO_SSH_FORWARD = "1337:127.0.0.1:1337"
UUID_RE = r"^[a-f0-9]{8}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{12}\Z" UUID_RE = r"^[a-f0-9]{8}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{12}\Z"
@ -1424,15 +1421,6 @@ class Scaleset(Endpoint):
if tags is None: if tags is None:
tags = {} tags = {}
if image is None:
pool = self.onefuzz.pools.get(pool_name)
if pool.os == enums.OS.linux:
image = DEFAULT_LINUX_IMAGE
elif pool.os == enums.OS.windows:
image = DEFAULT_WINDOWS_IMAGE
else:
raise NotImplementedError
auto_scale = requests.AutoScaleOptions( auto_scale = requests.AutoScaleOptions(
min=min_instances, min=min_instances,
max=max_size, max=max_size,

View File

@ -16,8 +16,6 @@ from onefuzztypes.primitives import Container, Directory, File
from ..job_templates.job_monitor import JobMonitor from ..job_templates.job_monitor import JobMonitor
ELF_MAGIC = b"\x7fELF" ELF_MAGIC = b"\x7fELF"
DEFAULT_LINUX_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest"
DEFAULT_WINDOWS_IMAGE = "MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest"
class StoppedEarly(Exception): class StoppedEarly(Exception):
@ -177,13 +175,6 @@ class JobHelper:
self.containers[ContainerType.inputs], Directory(tmp_dir) self.containers[ContainerType.inputs], Directory(tmp_dir)
) )
@classmethod
def get_image(_cls, platform: OS) -> str:
if platform == OS.linux:
return DEFAULT_LINUX_IMAGE
else:
return DEFAULT_WINDOWS_IMAGE
@classmethod @classmethod
def get_platform(_cls, target_exe: File) -> OS: def get_platform(_cls, target_exe: File) -> OS:
with open(target_exe, "rb") as handle: with open(target_exe, "rb") as handle:

View File

@ -880,6 +880,12 @@ class InstanceConfig(BaseModel):
default_factory=NetworkSecurityGroupConfig default_factory=NetworkSecurityGroupConfig
) )
extensions: Optional[AzureVmExtensionConfig] extensions: Optional[AzureVmExtensionConfig]
default_windows_vm_image: str = Field(
default="MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:latest"
)
default_linux_vm_image: str = Field(
default="Canonical:UbuntuServer:18.04-LTS:latest"
)
proxy_vm_sku: str = Field(default="Standard_B2s") proxy_vm_sku: str = Field(default="Standard_B2s")
api_access_rules: Optional[Dict[Endpoint, ApiAccessRule]] = None api_access_rules: Optional[Dict[Endpoint, ApiAccessRule]] = None
group_membership: Optional[Dict[PrincipalID, List[GroupId]]] = None group_membership: Optional[Dict[PrincipalID, List[GroupId]]] = None

View File

@ -182,7 +182,7 @@ class AutoScaleOptions(BaseModel):
class ScalesetCreate(BaseRequest): class ScalesetCreate(BaseRequest):
pool_name: PoolName pool_name: PoolName
vm_sku: str vm_sku: str
image: str image: Optional[str]
region: Optional[Region] region: Optional[Region]
size: int = Field(ge=1) size: int = Field(ge=1)
spot_instances: bool spot_instances: bool