// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.cloud.osconfig.v1;

import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/cloud/osconfig/v1/osconfig_common.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

option csharp_namespace = "Google.Cloud.OsConfig.V1";
option go_package = "google.golang.org/genproto/googleapis/cloud/osconfig/v1;osconfig";
option java_outer_classname = "PatchJobs";
option java_package = "com.google.cloud.osconfig.v1";
option php_namespace = "Google\\Cloud\\OsConfig\\V1";
option ruby_package = "Google::Cloud::OsConfig::V1";

// A request message to initiate patching across Compute Engine
// instances.
message ExecutePatchJobRequest {
  // Required. The project in which to run this patch in the form `projects/*`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "cloudresourcemanager.googleapis.com/Project"
    }
  ];

  // Description of the patch job. Length of the description is limited
  // to 1024 characters.
  string description = 2;

  // Required. Instances to patch, either explicitly or filtered by some
  // criteria such as zone or labels.
  PatchInstanceFilter instance_filter = 7
      [(google.api.field_behavior) = REQUIRED];

  // Patch configuration being applied. If omitted, instances are
  // patched using the default configurations.
  PatchConfig patch_config = 4;

  // Duration of the patch job. After the duration ends, the patch job
  // times out.
  google.protobuf.Duration duration = 5;

  // If this patch is a dry-run only, instances are contacted but
  // will do nothing.
  bool dry_run = 6;

  // Display name for this patch job. This does not have to be unique.
  string display_name = 8;

  // Rollout strategy of the patch job.
  PatchRollout rollout = 9;
}

// Request to get an active or completed patch job.
message GetPatchJobRequest {
  // Required. Name of the patch in the form `projects/*/patchJobs/*`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "osconfig.googleapis.com/PatchJob"
    }
  ];
}

// Request to list details for all instances that are part of a patch job.
message ListPatchJobInstanceDetailsRequest {
  // Required. The parent for the instances are in the form of
  // `projects/*/patchJobs/*`.
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "osconfig.googleapis.com/PatchJob"
    }
  ];

  // The maximum number of instance details records to return.  Default is 100.
  int32 page_size = 2;

  // A pagination token returned from a previous call
  // that indicates where this listing should continue from.
  string page_token = 3;

  // A filter expression that filters results listed in the response. This
  // field supports filtering results by instance zone, name, state, or
  // `failure_reason`.
  string filter = 4;
}

// A response message for listing the instances details for a patch job.
message ListPatchJobInstanceDetailsResponse {
  // A list of instance status.
  repeated PatchJobInstanceDetails patch_job_instance_details = 1;

  // A pagination token that can be used to get the next page of results.
  string next_page_token = 2;
}

// Patch details for a VM instance. For more information about reviewing VM
// instance details, see
// [Listing all VM instance details for a specific patch
// job](https://cloud.google.com/compute/docs/os-patch-management/manage-patch-jobs#list-instance-details).
message PatchJobInstanceDetails {
  // The instance name in the form `projects/*/zones/*/instances/*`
  string name = 1 [(google.api.resource_reference) = {
    type: "compute.googleapis.com/Instance"
  }];

  // The unique identifier for the instance. This identifier is
  // defined by the server.
  string instance_system_id = 2;

  // Current state of instance patch.
  Instance.PatchState state = 3;

  // If the patch fails, this field provides the reason.
  string failure_reason = 4;

  // The number of times the agent that the agent attempts to apply the patch.
  int64 attempt_count = 5;
}

// A request message for listing patch jobs.
message ListPatchJobsRequest {
  // Required. In the form of `projects/*`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "cloudresourcemanager.googleapis.com/Project"
    }
  ];

  // The maximum number of instance status to return.
  int32 page_size = 2;

  // A pagination token returned from a previous call
  // that indicates where this listing should continue from.
  string page_token = 3;

  // If provided, this field specifies the criteria that must be met by patch
  // jobs to be included in the response.
  // Currently, filtering is only available on the patch_deployment field.
  string filter = 4;
}

// A response message for listing patch jobs.
message ListPatchJobsResponse {
  // The list of patch jobs.
  repeated PatchJob patch_jobs = 1;

  // A pagination token that can be used to get the next page of results.
  string next_page_token = 2;
}

// A high level representation of a patch job that is either in progress
// or has completed.
//
// Instance details are not included in the job. To paginate through instance
// details, use ListPatchJobInstanceDetails.
//
// For more information about patch jobs, see
// [Creating patch
// jobs](https://cloud.google.com/compute/docs/os-patch-management/create-patch-job).
message PatchJob {
  option (google.api.resource) = {
    type: "osconfig.googleapis.com/PatchJob"
    pattern: "projects/{project}/patchJobs/{patch_job}"
  };

  // Enumeration of the various states a patch job passes through as it
  // executes.
  enum State {
    // State must be specified.
    STATE_UNSPECIFIED = 0;

    // The patch job was successfully initiated.
    STARTED = 1;

    // The patch job is looking up instances to run the patch on.
    INSTANCE_LOOKUP = 2;

    // Instances are being patched.
    PATCHING = 3;

    // Patch job completed successfully.
    SUCCEEDED = 4;

    // Patch job completed but there were errors.
    COMPLETED_WITH_ERRORS = 5;

    // The patch job was canceled.
    CANCELED = 6;

    // The patch job timed out.
    TIMED_OUT = 7;
  }

  // A summary of the current patch state across all instances that this patch
  // job affects. Contains counts of instances in different states. These states
  // map to `InstancePatchState`. List patch job instance details to see the
  // specific states of each instance.
  message InstanceDetailsSummary {
    // Number of instances pending patch job.
    int64 pending_instance_count = 1;

    // Number of instances that are inactive.
    int64 inactive_instance_count = 2;

    // Number of instances notified about patch job.
    int64 notified_instance_count = 3;

    // Number of instances that have started.
    int64 started_instance_count = 4;

    // Number of instances that are downloading patches.
    int64 downloading_patches_instance_count = 5;

    // Number of instances that are applying patches.
    int64 applying_patches_instance_count = 6;

    // Number of instances rebooting.
    int64 rebooting_instance_count = 7;

    // Number of instances that have completed successfully.
    int64 succeeded_instance_count = 8;

    // Number of instances that require reboot.
    int64 succeeded_reboot_required_instance_count = 9;

    // Number of instances that failed.
    int64 failed_instance_count = 10;

    // Number of instances that have acked and will start shortly.
    int64 acked_instance_count = 11;

    // Number of instances that exceeded the time out while applying the patch.
    int64 timed_out_instance_count = 12;

    // Number of instances that are running the pre-patch step.
    int64 pre_patch_step_instance_count = 13;

    // Number of instances that are running the post-patch step.
    int64 post_patch_step_instance_count = 14;

    // Number of instances that do not appear to be running the agent. Check to
    // ensure that the agent is installed, running, and able to communicate with
    // the service.
    int64 no_agent_detected_instance_count = 15;
  }

  // Unique identifier for this patch job in the form
  // `projects/*/patchJobs/*`
  string name = 1;

  // Display name for this patch job. This is not a unique identifier.
  string display_name = 14;

  // Description of the patch job. Length of the description is limited
  // to 1024 characters.
  string description = 2;

  // Time this patch job was created.
  google.protobuf.Timestamp create_time = 3;

  // Last time this patch job was updated.
  google.protobuf.Timestamp update_time = 4;

  // The current state of the PatchJob.
  State state = 5;

  // Instances to patch.
  PatchInstanceFilter instance_filter = 13;

  // Patch configuration being applied.
  PatchConfig patch_config = 7;

  // Duration of the patch job. After the duration ends, the
  // patch job times out.
  google.protobuf.Duration duration = 8;

  // Summary of instance details.
  InstanceDetailsSummary instance_details_summary = 9;

  // If this patch job is a dry run, the agent reports that it has
  // finished without running any updates on the VM instance.
  bool dry_run = 10;

  // If this patch job failed, this message provides information about the
  // failure.
  string error_message = 11;

  // Reflects the overall progress of the patch job in the range of
  // 0.0 being no progress to 100.0 being complete.
  double percent_complete = 12;

  // Output only. Name of the patch deployment that created this patch job.
  string patch_deployment = 15 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = {
      type: "osconfig.googleapis.com/PatchDeployment"
    }
  ];

  // Rollout strategy being applied.
  PatchRollout rollout = 16;
}

// Patch configuration specifications. Contains details on how to apply the
// patch(es) to a VM instance.
message PatchConfig {
  // Post-patch reboot settings.
  enum RebootConfig {
    // The default behavior is DEFAULT.
    REBOOT_CONFIG_UNSPECIFIED = 0;

    // The agent decides if a reboot is necessary by checking signals such as
    // registry keys on Windows or `/var/run/reboot-required` on APT based
    // systems. On RPM based systems, a set of core system package install times
    // are compared with system boot time.
    DEFAULT = 1;

    // Always reboot the machine after the update completes.
    ALWAYS = 2;

    // Never reboot the machine after the update completes.
    NEVER = 3;
  }

  // Post-patch reboot settings.
  RebootConfig reboot_config = 1;

  // Apt update settings. Use this setting to override the default `apt` patch
  // rules.
  AptSettings apt = 3;

  // Yum update settings. Use this setting to override the default `yum` patch
  // rules.
  YumSettings yum = 4;

  // Goo update settings. Use this setting to override the default `goo` patch
  // rules.
  GooSettings goo = 5;

  // Zypper update settings. Use this setting to override the default `zypper`
  // patch rules.
  ZypperSettings zypper = 6;

  // Windows update settings. Use this override the default windows patch rules.
  WindowsUpdateSettings windows_update = 7;

  // The `ExecStep` to run before the patch update.
  ExecStep pre_step = 8;

  // The `ExecStep` to run after the patch update.
  ExecStep post_step = 9;

  // Allows the patch job to run on Managed instance groups (MIGs).
  bool mig_instances_allowed = 10;
}

// Namespace for instance state enums.
message Instance {
  // Patch state of an instance.
  enum PatchState {
    // Unspecified.
    PATCH_STATE_UNSPECIFIED = 0;

    // The instance is not yet notified.
    PENDING = 1;

    // Instance is inactive and cannot be patched.
    INACTIVE = 2;

    // The instance is notified that it should be patched.
    NOTIFIED = 3;

    // The instance has started the patching process.
    STARTED = 4;

    // The instance is downloading patches.
    DOWNLOADING_PATCHES = 5;

    // The instance is applying patches.
    APPLYING_PATCHES = 6;

    // The instance is rebooting.
    REBOOTING = 7;

    // The instance has completed applying patches.
    SUCCEEDED = 8;

    // The instance has completed applying patches but a reboot is required.
    SUCCEEDED_REBOOT_REQUIRED = 9;

    // The instance has failed to apply the patch.
    FAILED = 10;

    // The instance acked the notification and will start shortly.
    ACKED = 11;

    // The instance exceeded the time out while applying the patch.
    TIMED_OUT = 12;

    // The instance is running the pre-patch step.
    RUNNING_PRE_PATCH_STEP = 13;

    // The instance is running the post-patch step.
    RUNNING_POST_PATCH_STEP = 14;

    // The service could not detect the presence of the agent. Check to ensure
    // that the agent is installed, running, and able to communicate with the
    // service.
    NO_AGENT_DETECTED = 15;
  }
}

// Message for canceling a patch job.
message CancelPatchJobRequest {
  // Required. Name of the patch in the form `projects/*/patchJobs/*`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "osconfig.googleapis.com/PatchJob"
    }
  ];
}

// Apt patching is completed by executing `apt-get update && apt-get
// upgrade`. Additional options can be set to control how this is executed.
message AptSettings {
  // Apt patch type.
  enum Type {
    // By default, upgrade will be performed.
    TYPE_UNSPECIFIED = 0;

    // Runs `apt-get dist-upgrade`.
    DIST = 1;

    // Runs `apt-get upgrade`.
    UPGRADE = 2;
  }

  // By changing the type to DIST, the patching is performed
  // using `apt-get dist-upgrade` instead.
  Type type = 1;

  // List of packages to exclude from update. These packages will be excluded
  repeated string excludes = 2;

  // An exclusive list of packages to be updated. These are the only packages
  // that will be updated. If these packages are not installed, they will be
  // ignored. This field cannot be specified with any other patch configuration
  // fields.
  repeated string exclusive_packages = 3;
}

// Yum patching is performed by executing `yum update`. Additional options
// can be set to control how this is executed.
//
// Note that not all settings are supported on all platforms.
message YumSettings {
  // Adds the `--security` flag to `yum update`. Not supported on
  // all platforms.
  bool security = 1;

  // Will cause patch to run `yum update-minimal` instead.
  bool minimal = 2;

  // List of packages to exclude from update. These packages are excluded by
  // using the yum `--exclude` flag.
  repeated string excludes = 3;

  // An exclusive list of packages to be updated. These are the only packages
  // that will be updated. If these packages are not installed, they will be
  // ignored. This field must not be specified with any other patch
  // configuration fields.
  repeated string exclusive_packages = 4;
}

// Googet patching is performed by running `googet update`.
message GooSettings {}

// Zypper patching is performed by running `zypper patch`.
// See also https://en.opensuse.org/SDB:Zypper_manual.
message ZypperSettings {
  // Adds the `--with-optional` flag to `zypper patch`.
  bool with_optional = 1;

  // Adds the `--with-update` flag, to `zypper patch`.
  bool with_update = 2;

  // Install only patches with these categories.
  // Common categories include security, recommended, and feature.
  repeated string categories = 3;

  // Install only patches with these severities.
  // Common severities include critical, important, moderate, and low.
  repeated string severities = 4;

  // List of patches to exclude from update.
  repeated string excludes = 5;

  // An exclusive list of patches to be updated. These are the only patches
  // that will be installed using 'zypper patch patch:<patch_name>' command.
  // This field must not be used with any other patch configuration fields.
  repeated string exclusive_patches = 6;
}

// Windows patching is performed using the Windows Update Agent.
message WindowsUpdateSettings {
  // Microsoft Windows update classifications as defined in
  // [1]
  // https://support.microsoft.com/en-us/help/824684/description-of-the-standard-terminology-that-is-used-to-describe-micro
  enum Classification {
    // Invalid. If classifications are included, they must be specified.
    CLASSIFICATION_UNSPECIFIED = 0;

    // "A widely released fix for a specific problem that addresses a critical,
    // non-security-related bug." [1]
    CRITICAL = 1;

    // "A widely released fix for a product-specific, security-related
    // vulnerability. Security vulnerabilities are rated by their severity. The
    // severity rating is indicated in the Microsoft security bulletin as
    // critical, important, moderate, or low." [1]
    SECURITY = 2;

    // "A widely released and frequent software update that contains additions
    // to a product's definition database. Definition databases are often used
    // to detect objects that have specific attributes, such as malicious code,
    // phishing websites, or junk mail." [1]
    DEFINITION = 3;

    // "Software that controls the input and output of a device." [1]
    DRIVER = 4;

    // "New product functionality that is first distributed outside the context
    // of a product release and that is typically included in the next full
    // product release." [1]
    FEATURE_PACK = 5;

    // "A tested, cumulative set of all hotfixes, security updates, critical
    // updates, and updates. Additionally, service packs may contain additional
    // fixes for problems that are found internally since the release of the
    // product. Service packs my also contain a limited number of
    // customer-requested design changes or features." [1]
    SERVICE_PACK = 6;

    // "A utility or feature that helps complete a task or set of tasks." [1]
    TOOL = 7;

    // "A tested, cumulative set of hotfixes, security updates, critical
    // updates, and updates that are packaged together for easy deployment. A
    // rollup generally targets a specific area, such as security, or a
    // component of a product, such as Internet Information Services (IIS)." [1]
    UPDATE_ROLLUP = 8;

    // "A widely released fix for a specific problem. An update addresses a
    // noncritical, non-security-related bug." [1]
    UPDATE = 9;
  }

  // Only apply updates of these windows update classifications. If empty, all
  // updates are applied.
  repeated Classification classifications = 1;

  // List of KBs to exclude from update.
  repeated string excludes = 2;

  // An exclusive list of kbs to be updated. These are the only patches
  // that will be updated. This field must not be used with other
  // patch configurations.
  repeated string exclusive_patches = 3;
}

// A step that runs an executable for a PatchJob.
message ExecStep {
  // The ExecStepConfig for all Linux VMs targeted by the PatchJob.
  ExecStepConfig linux_exec_step_config = 1;

  // The ExecStepConfig for all Windows VMs targeted by the PatchJob.
  ExecStepConfig windows_exec_step_config = 2;
}

// Common configurations for an ExecStep.
message ExecStepConfig {
  // The interpreter used to execute the a file.
  enum Interpreter {
    // Invalid for a Windows ExecStepConfig. For a Linux ExecStepConfig, the
    // interpreter will be parsed from the shebang line of the script if
    // unspecified.
    INTERPRETER_UNSPECIFIED = 0;

    // Indicates that the script is run with `/bin/sh` on Linux and `cmd`
    // on Windows.
    SHELL = 1;

    // Indicates that the file is run with PowerShell flags
    // `-NonInteractive`, `-NoProfile`, and `-ExecutionPolicy Bypass`.
    POWERSHELL = 2;
  }

  // Location of the executable.
  oneof executable {
    // An absolute path to the executable on the VM.
    string local_path = 1;

    // A Cloud Storage object containing the executable.
    GcsObject gcs_object = 2;
  }

  // Defaults to [0]. A list of possible return values that the
  // execution can return to indicate a success.
  repeated int32 allowed_success_codes = 3;

  // The script interpreter to use to run the script. If no interpreter is
  // specified the script will be executed directly, which will likely
  // only succeed for scripts with [shebang lines]
  // (https://en.wikipedia.org/wiki/Shebang_\(Unix\)).
  Interpreter interpreter = 4;
}

// Cloud Storage object representation.
message GcsObject {
  // Required. Bucket of the Cloud Storage object.
  string bucket = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Name of the Cloud Storage object.
  string object = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. Generation number of the Cloud Storage object. This is used to
  // ensure that the ExecStep specified by this PatchJob does not change.
  int64 generation_number = 3 [(google.api.field_behavior) = REQUIRED];
}

// A filter to target VM instances for patching. The targeted
// VMs must meet all criteria specified. So if both labels and zones are
// specified, the patch job targets only VMs with those labels and in those
// zones.
message PatchInstanceFilter {
  // Targets a group of VM instances by using their [assigned
  // labels](https://cloud.google.com/compute/docs/labeling-resources). Labels
  // are key-value pairs. A `GroupLabel` is a combination of labels
  // that is used to target VMs for a patch job.
  //
  // For example, a patch job can target VMs that have the following
  // `GroupLabel`: `{"env":"test", "app":"web"}`. This means that the patch job
  // is applied to VMs that have both the labels `env=test` and `app=web`.
  message GroupLabel {
    // Compute Engine instance labels that must be present for a VM
    // instance to be targeted by this filter.
    map<string, string> labels = 1;
  }

  // Target all VM instances in the project. If true, no other criteria is
  // permitted.
  bool all = 1;

  // Targets VM instances matching ANY of these GroupLabels. This allows
  // targeting of disparate groups of VM instances.
  repeated GroupLabel group_labels = 2;

  // Targets VM instances in ANY of these zones. Leave empty to target VM
  // instances in any zone.
  repeated string zones = 3;

  // Targets any of the VM instances specified. Instances are specified by their
  // URI in the form `zones/[ZONE]/instances/[INSTANCE_NAME]`,
  // `projects/[PROJECT_ID]/zones/[ZONE]/instances/[INSTANCE_NAME]`, or
  // `https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[INSTANCE_NAME]`
  repeated string instances = 4;

  // Targets VMs whose name starts with one of these prefixes. Similar to
  // labels, this is another way to group VMs when targeting configs, for
  // example prefix="prod-".
  repeated string instance_name_prefixes = 5;
}

// Patch rollout configuration specifications. Contains details on the
// concurrency control when applying patch(es) to all targeted VMs.
message PatchRollout {
  // Type of the rollout.
  enum Mode {
    // Mode must be specified.
    MODE_UNSPECIFIED = 0;

    // Patches are applied one zone at a time. The patch job begins in the
    // region with the lowest number of targeted VMs. Within the region,
    // patching begins in the zone with the lowest number of targeted VMs. If
    // multiple regions (or zones within a region) have the same number of
    // targeted VMs, a tie-breaker is achieved by sorting the regions or zones
    // in alphabetical order.
    ZONE_BY_ZONE = 1;

    // Patches are applied to VMs in all zones at the same time.
    CONCURRENT_ZONES = 2;
  }

  // Mode of the patch rollout.
  Mode mode = 1;

  // The maximum number (or percentage) of VMs per zone to disrupt at any given
  // moment. The number of VMs calculated from multiplying the percentage by the
  // total number of VMs in a zone is rounded up.
  //
  // During patching, a VM is considered disrupted from the time the agent is
  // notified to begin until patching has completed. This disruption time
  // includes the time to complete reboot and any post-patch steps.
  //
  // A VM contributes to the disruption budget if its patching operation fails
  // either when applying the patches, running pre or post patch steps, or if it
  // fails to respond with a success notification before timing out. VMs that
  // are not running or do not have an active agent do not count toward this
  // disruption budget.
  //
  // For zone-by-zone rollouts, if the disruption budget in a zone is exceeded,
  // the patch job stops, because continuing to the next zone requires
  // completion of the patch process in the previous zone.
  //
  // For example, if the disruption budget has a fixed value of `10`, and 8 VMs
  // fail to patch in the current zone, the patch job continues to patch 2 VMs
  // at a time until the zone is completed. When that zone is completed
  // successfully, patching begins with 10 VMs at a time in the next zone. If 10
  // VMs in the next zone fail to patch, the patch job stops.
  FixedOrPercent disruption_budget = 2;
}