Esempio n. 1
0
    public async Async.Task <RegressionReportOrReport?> GetReportOrRegression(Container container, string fileName, bool expectReports = false, params string[] args)
    {
        var filePath = String.Join("/", new[] { container.ContainerName, fileName });

        if (!fileName.EndsWith(".json"))
        {
            if (expectReports)
            {
                _log.Error($"get_report invalid extension: {filePath}");
            }
            return(null);
        }

        var blob = await _containers.GetBlob(container, fileName, StorageType.Corpus);

        if (blob == null)
        {
            if (expectReports)
            {
                _log.Error($"get_report invalid blob: {filePath}");
            }
            return(null);
        }

        return(ParseReportOrRegression(blob.ToString(), filePath, expectReports));
    }
Esempio n. 2
0
    async Async.Task SetState(Scaleset scaleSet, ScalesetState state)
    {
        if (scaleSet.State == state)
        {
            return;
        }

        if (scaleSet.State == ScalesetState.Halt)
        {
            return;
        }

        var updatedScaleSet = scaleSet with {
            State = state
        };
        var r = await this.Replace(updatedScaleSet);

        if (!r.IsOk)
        {
            _log.Error($"Failed to update scaleset {scaleSet.ScalesetId} when updating state from {scaleSet.State} to {state}");
        }

        if (state == ScalesetState.Resize)
        {
            await _context.Events.SendEvent(
                new EventScalesetResizeScheduled(updatedScaleSet.ScalesetId, updatedScaleSet.PoolName, updatedScaleSet.Size)
                );
        }
        else
        {
            await _context.Events.SendEvent(
                new EventScalesetStateUpdated(updatedScaleSet.ScalesetId, updatedScaleSet.PoolName, updatedScaleSet.State)
                );
        }
    }

    async Async.Task SetFailed(Scaleset scaleSet, Error error)
    {
        if (scaleSet.Error is not null)
        {
            return;
        }

        await SetState(scaleSet with {
            Error = error
        }, ScalesetState.CreationFailed);

        await _context.Events.SendEvent(new EventScalesetFailed(scaleSet.ScalesetId, scaleSet.PoolName, error));
    }
Esempio n. 3
0
    public async Task <BlobContainerClient?> GetOrCreateContainerClient(Container container, StorageType storageType, IDictionary <string, string>?metadata)
    {
        var containerClient = await FindContainer(container, StorageType.Corpus);

        if (containerClient is not null)
        {
            return(containerClient);
        }

        var account = _storage.ChooseAccount(storageType);
        var client  = await _storage.GetBlobServiceClientForAccount(account);

        var containerName = _config.OneFuzzStoragePrefix + container.ContainerName;
        var cc            = client.GetBlobContainerClient(containerName);

        try {
            await cc.CreateAsync(metadata : metadata);
        } catch (RequestFailedException ex) when(ex.ErrorCode == "ContainerAlreadyExists")
        {
            // note: resource exists error happens during creation if the container
            // is being deleted
            _log.Error($"unable to create container.  account: {account} container: {container.ContainerName} metadata: {metadata} - {ex.Message}");
            return(null);
        }

        return(cc);
    }
Esempio n. 4
0
    public async Async.Task <IDictionary <Guid, string> > ListInstanceIds(Guid name)
    {
        _log.Verbose($"get instance IDs for scaleset {name}");
        var results = new Dictionary <Guid, string>();
        VirtualMachineScaleSetResource res;

        try {
            var r = await GetVmssResource(name).GetAsync();

            res = r.Value;
        } catch (Exception ex) when(ex is RequestFailedException)
        {
            _log.Verbose($"vm does not exist {name}");
            return(results);
        }

        if (res is null)
        {
            _log.Verbose($"vm does not exist {name}");
            return(results);
        }
        else
        {
            try {
                await foreach (var instance in res !.GetVirtualMachineScaleSetVms().AsAsyncEnumerable())
                {
                    if (instance is not null)
                    {
                        Guid key;
                        if (Guid.TryParse(instance.Data.VmId, out key))
                        {
                            results[key] = instance.Data.InstanceId;
                        }
                        else
                        {
                            _log.Error($"failed to convert vmId {instance.Data.VmId} to Guid");
                        }
                    }
                }
            } catch (Exception ex) when(ex is RequestFailedException || ex is CloudException)
            {
                _log.Exception(ex, $"vm does not exist {name}");
            }
        }
        return(results);
    }
Esempio n. 5
0
    public async Task <OneFuzzResult <bool> > AddExtensions(Vm vm, Dictionary <string, VirtualMachineExtensionData> extensions)
    {
        var status   = new List <string>();
        var toCreate = new List <KeyValuePair <string, VirtualMachineExtensionData> >();

        foreach (var extensionConfig in extensions)
        {
            var extensionName = extensionConfig.Key;
            var extensionData = extensionConfig.Value;

            var extension = await GetExtension(vm.Name, extensionName);

            if (extension != null)
            {
                _logTracer.Info(
                    $"vm extension state: {vm.Name} - {extensionName} - {extension.ProvisioningState}"
                    );
                status.Add(extension.ProvisioningState);
            }
            else
            {
                toCreate.Add(extensionConfig);
            }
        }

        if (toCreate.Any())
        {
            foreach (var config in toCreate)
            {
                await CreateExtension(vm.Name, config.Key, config.Value);
            }
        }
        else
        {
            if (status.All(s => string.Equals(s, "Succeeded", StringComparison.Ordinal)))
            {
                return(OneFuzzResult <bool> .Ok(true));
            }
            else if (status.Any(s => string.Equals(s, "Failed", StringComparison.Ordinal)))
            {
                return(OneFuzzResult <bool> .Error(
                           ErrorCode.VM_CREATE_FAILED,
                           "failed to launch extension"
                           ));
            }
            else if (!(status.Contains("Creating") || status.Contains("Updating")))
            {
                _logTracer.Error($"vm agent - unknown state {vm.Name}: {JsonConvert.SerializeObject(status)}");
            }
        }

        return(OneFuzzResult <bool> .Ok(false));
    }
Esempio n. 6
0
    public async Async.Task SyncScalesetSize(Scaleset scaleset)
    {
        // # If our understanding of size is out of sync with Azure, resize the
        // # scaleset to match our understanding.
        if (scaleset.State != ScalesetState.Running)
        {
            return;
        }

        var size = await _context.VmssOperations.GetVmssSize(scaleset.ScalesetId);

        if (size is null)
        {
            _log.Info($"{SCALESET_LOG_PREFIX} scaleset is unavailable. scaleset_id: {scaleset.ScalesetId}");
            //#if the scaleset is missing, this is an indication the scaleset
            //# was manually deleted, rather than having OneFuzz delete it.  As
            //# such, we should go thruogh the process of deleting it.
            await SetShutdown(scaleset, now : true);

            return;
        }
        if (size != scaleset.Size)
        {
            //# Azure auto-scaled us or nodes were manually added/removed
            //# New node state will be synced in cleanup_nodes
            _log.Info($"{SCALESET_LOG_PREFIX} unexpected scaleset size, resizing. scaleset_id: {scaleset.ScalesetId} expected:{scaleset.Size} actual:{size}");

            scaleset = scaleset with {
                Size = size.Value
            };
            var replaceResult = await Update(scaleset);

            if (!replaceResult.IsOk)
            {
                _log.Error($"Failed to update scaleset size for scaleset {scaleset.ScalesetId} due to {replaceResult.ErrorV}");
            }
        }
    }
Esempio n. 7
0
    private async Async.Task <BlobServiceClient?> GetBlobService(string accountId)
    {
        _log.Info($"getting blob container (account_id: {accountId}");
        var(accountName, accountKey) = await _storage.GetStorageAccountNameAndKey(accountId);

        if (accountName == null)
        {
            _log.Error("Failed to get storage account name");
            return(null);
        }
        var storageKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
        var accountUrl           = GetUrl(accountName);

        return(new BlobServiceClient(accountUrl, storageKeyCredential));
    }
Esempio n. 8
0
    public async Async.Task <HttpResponseData> NotOk(HttpRequestData request, Error error, string context, HttpStatusCode statusCode = HttpStatusCode.BadRequest)
    {
        var statusNum = (int)statusCode;

        if (statusNum >= 400 && statusNum <= 599)
        {
            _log.Error($"request error - {context}: {error}");

            var response = HttpResponseData.CreateResponse(request);
            await response.WriteAsJsonAsync(error);

            response.StatusCode = statusCode;
            return(response);
        }

        throw new ArgumentOutOfRangeException($"status code {statusCode} - {statusNum} is not in the expected range [400; 599]");
    }
Esempio n. 9
0
    private async Async.Task <bool> ScheduleWorkset(WorkSet workSet, Pool pool, long count)
    {
        if (!PoolStateHelper.Available.Contains(pool.State))
        {
            _logTracer.Info($"pool not available for work: {pool.Name} state: {pool.State}");
            return(false);
        }

        for (var i = 0L; i < count; i++)
        {
            if (!await _poolOperations.ScheduleWorkset(pool, workSet))
            {
                _logTracer.Error($"unable to schedule workset. pool:{pool.Name} workset: {workSet}");
                return(false);
            }
        }
        return(true);
    }
Esempio n. 10
0
    public async Task <bool> DeleteDisk(string resourceGroup, string name)
    {
        try {
            _logTracer.Info($"deleting disks {resourceGroup} : {name}");
            var disk = await _creds.GetResourceGroupResource().GetDiskAsync(name);

            if (disk != null)
            {
                await disk.Value.DeleteAsync(WaitUntil.Started);

                return(true);
            }
        } catch (Exception e) {
            _logTracer.Error($"unable to delete disk: {name} {e.Message}");
            _logTracer.Exception(e);
        }
        return(false);
    }
Esempio n. 11
0
    public async Task <OneFuzzResultVoid> CreateVirtualNetwork(string resourceGroup, string name, string region, NetworkConfig networkConfig)
    {
        _logTracer.Info($"creating subnet - resource group:{resourceGroup} name:{name} region: {region}");

        var virtualNetParam = new VirtualNetworkData {
            Location = region,
        };

        virtualNetParam.AddressPrefixes.Add(networkConfig.AddressSpace);
        virtualNetParam.Subnets.Add(new SubnetData {
            Name          = name,
            AddressPrefix = networkConfig.Subnet
        }
                                    );

        var onefuzzOwner = _context.ServiceConfiguration.OneFuzzOwner;

        if (!string.IsNullOrEmpty(onefuzzOwner))
        {
            if (!virtualNetParam.Tags.TryAdd("OWNER", onefuzzOwner))
            {
                _logTracer.Warning($"Failed to add tag 'OWNER':{onefuzzOwner} to virtual network {resourceGroup}:{name}");
            }
        }

        try {
            await _creds.GetResourceGroupResource().GetVirtualNetworks().CreateOrUpdateAsync(
                WaitUntil.Started,
                name,
                virtualNetParam
                );
        } catch (RequestFailedException ex) {
            _logTracer.Error($"network creation failed: {name}:{region} {{error}}");
            return(OneFuzzResultVoid.Error(
                       ErrorCode.UNABLE_TO_CREATE_NETWORK,
                       ex.ToString()
                       ));
        }

        return(OneFuzzResultVoid.Ok);
    }
Esempio n. 12
0
    public async Task <HttpResponseData> SaveToKeyvault([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "testhooks/secrets/keyvault")] HttpRequestData req)
    {
        var s = await req.ReadAsStringAsync();

        var secretData = JsonSerializer.Deserialize <SecretData <string> >(s !, EntityConverter.GetJsonSerializerOptions());

        if (secretData is null)
        {
            _log.Error("Secret data is null");
            return(req.CreateResponse(HttpStatusCode.BadRequest));
        }
        else
        {
            _log.Info($"Saving secret data in the keyvault");
            var r = await _secretOps.SaveToKeyvault(secretData);

            var addr = _secretOps.GetKeyvaultAddress();
            var resp = req.CreateResponse(HttpStatusCode.OK);
            await resp.WriteAsJsonAsync(addr);

            return(resp);
        }
    }
Esempio n. 13
0
        public async Task <HttpResponseData> Get([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/instance-config")] HttpRequestData req)
        {
            _log.Info("Fetching instance config");
            var config = await _configOps.Fetch();

            if (config is null)
            {
                _log.Error("Instance config is null");
                Error err  = new(ErrorCode.INVALID_REQUEST, new[] { "Instance config is null" });
                var   resp = req.CreateResponse(HttpStatusCode.InternalServerError);
                await resp.WriteAsJsonAsync(err);

                return(resp);
            }
            else
            {
                var str = EntityConverter.ToJsonString(config);

                var resp = req.CreateResponse(HttpStatusCode.OK);
                await resp.WriteStringAsync(str);

                return(resp);
            }
        }
Esempio n. 14
0
    public async Async.Task SetSize(int size)
    {
        await Clear();

        var i = 0;

        while (i < size)
        {
            var r = await AddEntry();

            if (r)
            {
                i++;
            }
            else
            {
                //TODO: retry after a delay ? I guess make a decision on this
                //if we hit this error message... For now just log and move on to
                //make it behave same as Python code.
                _log.Error($"failed to add entry to shrink queue");
                i++;
            }
        }
    }
Esempio n. 15
0
    public async Task <bool> QueueObject <T>(string name, T obj, StorageType storageType, TimeSpan?visibilityTimeout = null, TimeSpan?timeToLive = null)
    {
        var queueClient = await GetQueueClient(name, storageType) ?? throw new Exception($"unable to queue object, no such queue: {name}");

        try {
            var serialized = JsonSerializer.Serialize(obj, EntityConverter.GetJsonSerializerOptions());
            var res        = await queueClient.SendMessageAsync(serialized, visibilityTimeout : visibilityTimeout, timeToLive);

            if (res.GetRawResponse().IsError)
            {
                _log.Error($"Failed to send message {serialized} in queue {name} due to {res.GetRawResponse().ReasonPhrase}");
                return(false);
            }
            else
            {
                return(true);
            }
        } catch (Exception ex) {
            _log.Exception(ex, $"Failed to queue message in queue {name}");
            return(false);
        }
    }
Esempio n. 16
0
    private async Async.Task <Error?> OnStateUpdate(Guid machineId, NodeStateUpdate ev)
    {
        var node = await _context.NodeOperations.GetByMachineId(machineId);

        if (node is null)
        {
            _log.Warning($"unable to process state update event. machine_id:{machineId} state event:{ev}");
            return(null);
        }

        if (ev.State == NodeState.Free)
        {
            if (node.ReimageRequested || node.DeleteRequested)
            {
                _log.Info($"stopping free node with reset flags: {machineId}");
                await _context.NodeOperations.Stop(node);

                return(null);
            }

            if (await _context.NodeOperations.CouldShrinkScaleset(node))
            {
                _log.Info($"stopping free node to resize scaleset: {machineId}");
                await _context.NodeOperations.SetHalt(node);

                return(null);
            }
        }

        if (ev.State == NodeState.Init)
        {
            if (node.DeleteRequested)
            {
                _log.Info($"stopping node (init and delete_requested): {machineId}");
                await _context.NodeOperations.Stop(node);

                return(null);
            }

            // Don’t check reimage_requested, as nodes only send 'init' state once.  If
            // they send 'init' with reimage_requested, it's because the node was reimaged
            // successfully.
            node = node with {
                ReimageRequested = false, InitializedAt = DateTimeOffset.UtcNow
            };
            await _context.NodeOperations.SetState(node, ev.State);

            return(null);
        }

        _log.Info($"node state update: {machineId} from {node.State} to {ev.State}");
        await _context.NodeOperations.SetState(node, ev.State);

        if (ev.State == NodeState.Free)
        {
            _log.Info($"node now available for work: {machineId}");
        }
        else if (ev.State == NodeState.SettingUp)
        {
            if (ev.Data is NodeSettingUpEventData settingUpData)
            {
                if (!settingUpData.Tasks.Any())
                {
                    return(new Error(ErrorCode.INVALID_REQUEST, Errors: new string[] {
                        $"setup without tasks.  machine_id: {machineId}",
                    }));
                }

                foreach (var taskId in settingUpData.Tasks)
                {
                    var task = await _context.TaskOperations.GetByTaskId(taskId);

                    if (task is null)
                    {
                        return(new Error(
                                   ErrorCode.INVALID_REQUEST,
                                   Errors: new string[] { $"unable to find task: {taskId}" }));
                    }

                    _log.Info($"node starting task.  machine_id: {machineId} job_id: {task.JobId} task_id: {task.TaskId}");

                    // The task state may be `running` if it has `vm_count` > 1, and
                    // another node is concurrently executing the task. If so, leave
                    // the state as-is, to represent the max progress made.
                    //
                    // Other states we would want to preserve are excluded by the
                    // outermost conditional check.
                    if (task.State != TaskState.Running && task.State != TaskState.SettingUp)
                    {
                        await _context.TaskOperations.SetState(task, TaskState.SettingUp);
                    }

                    var nodeTask = new NodeTasks(
                        MachineId: machineId,
                        TaskId: task.TaskId,
                        State: NodeTaskState.SettingUp);
                    await _context.NodeTasksOperations.Replace(nodeTask);
                }
            }
        }
        else if (ev.State == NodeState.Done)
        {
            Error?error = null;
            if (ev.Data is NodeDoneEventData doneData)
            {
                if (doneData.Error is not null)
                {
                    var errorText = EntityConverter.ToJsonString(doneData);
                    error = new Error(ErrorCode.TASK_FAILED, Errors: new string[] { errorText });
                    _log.Error($"node 'done' with error: machine_id:{machineId}, data:{errorText}");
                }
            }

            // if tasks are running on the node when it reports as Done
            // those are stopped early
            await _context.NodeOperations.MarkTasksStoppedEarly(node, error);

            await _context.NodeOperations.ToReimage(node, done : true);
        }

        return(null);
    }
Esempio n. 17
0
    //[Function("TimerProxy")]
    public async Async.Task Run([TimerTrigger("1.00:00:00")] TimerInfo myTimer)
    {
        var proxies = await _proxYOperations.QueryAsync().ToListAsync();

        foreach (var proxy in proxies)
        {
            if (VmStateHelper.Available.Contains(proxy.State))
            {
                // Note, outdated checked at the start, but set at the end of this loop.
                // As this function is called via a timer, this works around a user
                // requesting to use the proxy while this function is checking if it's
                // out of date
                if (proxy.Outdated)
                {
                    await _proxYOperations.SetState(proxy, VmState.Stopping);

                    // If something is "wrong" with a proxy, delete & recreate it
                }
                else if (!_proxYOperations.IsAlive(proxy))
                {
                    _logger.Error($"scaleset-proxy: alive check failed, stopping: {proxy.Region}");
                    await _proxYOperations.SetState(proxy, VmState.Stopping);
                }
                else
                {
                    await _proxYOperations.SaveProxyConfig(proxy);
                }
            }

            if (VmStateHelper.NeedsWork.Contains(proxy.State))
            {
                _logger.Error($"scaleset-proxy: update state. proxy:{proxy.Region} state:{proxy.State}");
                await _proxYOperations.ProcessStateUpdate(proxy);
            }

            if (proxy.State != VmState.Stopped && _proxYOperations.IsOutdated(proxy))
            {
                await _proxYOperations.Replace(proxy with {
                    Outdated = true
                });
            }
        }

        // make sure there is a proxy for every currently active region
        var regions = await _scalesetOperations.QueryAsync().Select(x => x.Region).ToHashSetAsync();

        foreach (var region in regions)
        {
            var allOutdated = proxies.Where(x => x.Region == region).All(p => p.Outdated);
            if (allOutdated)
            {
                await _proxYOperations.GetOrCreate(region);

                _logger.Info($"Creating new proxy in region {region}");
            }

            // this is required in order to support upgrade from non-nsg to
            // nsg enabled OneFuzz this will overwrite existing NSG
            // assignment though. This behavior is acceptable at this point
            // since we do not support bring your own NSG

            if (await _nsg.GetNsg(region) != null)
            {
                var network = await Network.Create(region, _creds, _configOperations, _subnet);

                var subnet = await network.GetSubnet();

                var vnet = await network.GetVnet();

                if (subnet != null && vnet != null)
                {
                    var error = _nsg.AssociateSubnet(region, vnet, subnet);
                    if (error != null)
                    {
                        _logger.Error($"Failed to associate NSG and subnet due to {error} in region {region}");
                    }
                }
            }

            // if there are NSGs with name same as the region that they are allocated
            // and have no NIC associated with it then delete the NSG
            await foreach (var nsg in _nsg.ListNsgs())
            {
                if (_nsg.OkToDelete(regions, nsg.Data.Location, nsg.Data.Name))
                {
                    if (nsg.Data.NetworkInterfaces.Count == 0 && nsg.Data.Subnets.Count == 0)
                    {
                        await _nsg.StartDeleteNsg(nsg.Data.Name);
                    }
                }
            }
        }
    }
Esempio n. 18
0
    public async Async.Task <ResultVoid <TaskConfigError> > CheckConfig(TaskConfig config)
    {
        if (!Defs.TASK_DEFINITIONS.ContainsKey(config.Task.Type))
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError($"unsupported task type: {config.Task.Type}")));
        }

        if (config.Vm != null && config.Pool != null)
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError($"either the vm or pool must be specified, but not both")));
        }

        var definition = Defs.TASK_DEFINITIONS[config.Task.Type];
        var r          = await CheckContainers(definition, config);

        if (!r.IsOk)
        {
            return(r);
        }

        if (definition.Features.Contains(TaskFeature.SupervisorExe) && config.Task.SupervisorExe == null)
        {
            var err = "missing supervisor_exe";
            _logTracer.Error(err);
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError(err)));
        }

        if (definition.Features.Contains(TaskFeature.TargetMustUseInput) && !TargetUsesInput(config))
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError("{input} must be used in target_env or target_options")));
        }

        if (config.Vm != null)
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError("specifying task config vm is no longer supported")));
        }

        if (config.Pool == null)
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError("pool must be specified")));
        }

        if (!CheckVal(definition.Vm.Compare, definition.Vm.Value, config.Pool !.Count))
        {
            var err =
                $"invalid vm count: expected {definition.Vm.Compare} {definition.Vm.Value}, got {config.Pool.Count}";
            _logTracer.Error(err);
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError(err)));
        }

        var pool = await _context.PoolOperations.GetByName(config.Pool.PoolName);

        if (!pool.IsOk)
        {
            return(ResultVoid <TaskConfigError> .Error(new TaskConfigError($"invalid pool: {config.Pool.PoolName}")));
        }

        var checkTarget = await CheckTargetExe(config, definition);

        if (!checkTarget.IsOk)
        {
            return(checkTarget);
        }

        if (definition.Features.Contains(TaskFeature.GeneratorExe))
        {
            var container = config.Containers !.First(x => x.Type == ContainerType.Tools);

            if (config.Task.GeneratorExe == null)
            {
                return(ResultVoid <TaskConfigError> .Error(new TaskConfigError($"generator_exe is not defined")));
            }

            var tool_paths = new[] { "{tools_dir}/", "{tools_dir}\\" };

            foreach (var toolPath in tool_paths)
            {
                if (config.Task.GeneratorExe.StartsWith(toolPath))
                {
                    var generator = config.Task.GeneratorExe.Replace(toolPath, "");
                    if (!await _containers.BlobExists(container.Name, generator, StorageType.Corpus))
                    {
                        var err =
                            $"generator_exe `{config.Task.GeneratorExe}` does not exist in the tools container `{container.Name}`";
                        _logTracer.Error(err);
                        return(ResultVoid <TaskConfigError> .Error(new TaskConfigError(err)));
                    }
                }
            }
        }

        if (definition.Features.Contains(TaskFeature.StatsFile))
        {
            if (config.Task.StatsFile != null && config.Task.StatsFormat == null)
            {
                var err2 = "using a stats_file requires a stats_format";
                _logTracer.Error(err2);
                return(ResultVoid <TaskConfigError> .Error(new TaskConfigError(err2)));
            }
        }

        return(ResultVoid <TaskConfigError> .Ok());
    }