...@@ -32,12 +32,12 @@ The `BulkInsertSafe` concern has two functions: ...@@ -32,12 +32,12 @@ The `BulkInsertSafe` concern has two functions:
- It performs checks against your model class to ensure that it does not use ActiveRecord - It performs checks against your model class to ensure that it does not use ActiveRecord
APIs that are not safe to use with respect to bulk insertions (more on that below). APIs that are not safe to use with respect to bulk insertions (more on that below).
- It adds a new class method `bulk_insert!`, which you can use to insert many records at once. - It adds new class methods `bulk_insert!` and `bulk_upsert!`, which you can use to insert many records at once.
## Insert records via `bulk_insert!` ## Insert records with `bulk_insert!` and `bulk_upsert!`
If the target class passes the checks performed by `BulkInsertSafe`, you can proceed to use If the target class passes the checks performed by `BulkInsertSafe`, you can insert an array of
the `bulk_insert!` class method as follows: ActiveRecord model objects as follows:
```ruby ```ruby
records = [MyModel.new, ...] records = [MyModel.new, ...]
...@@ -45,6 +45,28 @@ records = [MyModel.new, ...] ...@@ -45,6 +45,28 @@ records = [MyModel.new, ...]
MyModel.bulk_insert!(records) MyModel.bulk_insert!(records)
``` ```
Note that calls to `bulk_insert!` will always attempt to insert _new records_. If instead
you would like to replace existing records with new values, while still inserting those
that do not already exist, then you can use `bulk_upsert!`:
```ruby
records = [MyModel.new, existing_model, ...]
MyModel.bulk_upsert!(records, unique_by: [:name])
```
In this example, `unique_by` specifies the columns by which records are considered to be
unique and as such will be updated if they existed prior to insertion. For example, if
`existing_model` has a `name` attribute, and if a record with the same `name` value already
exists, its fields will be updated with those of `existing_model`.
The `unique_by` parameter can also be passed as a `Symbol`, in which case it specifies
a database index by which a column is considered unique:
```ruby
MyModel.bulk_insert!(records, unique_by: :index_on_name)
```
### Record validation ### Record validation
The `bulk_insert!` method guarantees that `records` will be inserted transactionally, and The `bulk_insert!` method guarantees that `records` will be inserted transactionally, and
...@@ -74,6 +96,23 @@ Since this will also affect the number of `INSERT`s that occur, make sure you me ...@@ -74,6 +96,23 @@ Since this will also affect the number of `INSERT`s that occur, make sure you me
performance impact this might have on your code. There is a trade-off between the number of performance impact this might have on your code. There is a trade-off between the number of
`INSERT` statements the database has to process and the size and cost of each `INSERT`. `INSERT` statements the database has to process and the size and cost of each `INSERT`.
### Handling duplicate records
NOTE: **Note:**
This parameter applies only to `bulk_insert!`. If you intend to update existing
records, use `bulk_upsert!` instead.
It may happen that some records you are trying to insert already exist, which would result in
primary key conflicts. There are two ways to address this problem: failing fast by raising an
error or skipping duplicate records. The default behavior of `bulk_insert!` is to fail fast
and raise an `ActiveRecord::RecordNotUnique` error.
If this is undesirable, you can instead skip duplicate records with the `skip_duplicates` flag:
```ruby
MyModel.bulk_insert!(records, skip_duplicates: true)
```
### Requirements for safe bulk insertions ### Requirements for safe bulk insertions
Large parts of ActiveRecord's persistence API are built around the notion of callbacks. Many Large parts of ActiveRecord's persistence API are built around the notion of callbacks. Many
...@@ -145,11 +184,12 @@ simply be treated as if you had invoked `save` from outside the block. ...@@ -145,11 +184,12 @@ simply be treated as if you had invoked `save` from outside the block.
There are a few restrictions to how these APIs can be used: There are a few restrictions to how these APIs can be used:
- Bulk inserts only work for new records; `UPDATE`s or "upserts" are not supported yet.
- `ON CONFLICT` behavior cannot currently be configured; an error will be raised on primary key conflicts. - `ON CONFLICT` behavior cannot currently be configured; an error will be raised on primary key conflicts.
- `BulkInsertableAssociations` furthermore has the following restrictions: - `BulkInsertableAssociations` furthermore has the following restrictions:
- only compatible with `has_many` relations. - only compatible with `has_many` relations.
- does not support `has_many through: ...` relations. - does not support `has_many through: ...` relations.
- Writing [`jsonb`](https://www.postgresql.org/docs/current/datatype-json.html) content is
[not currently supported](https://gitlab.com/gitlab-org/gitlab/-/issues/210560).
Moreover, input data should either be limited to around 1000 records at most, Moreover, input data should either be limited to around 1000 records at most,
or already batched prior to calling bulk insert. The `INSERT` statement will run in a single or already batched prior to calling bulk insert. The `INSERT` statement will run in a single
... ...
......
...@@ -144,10 +144,20 @@ It's also important to ensure that any background migrations have been fully com ...@@ -144,10 +144,20 @@ It's also important to ensure that any background migrations have been fully com
before upgrading to a new major version. To see the current size of the `background_migration` queue, before upgrading to a new major version. To see the current size of the `background_migration` queue,
[Check for background migrations before upgrading](../update/README.md#checking-for-background-migrations-before-upgrading). [Check for background migrations before upgrading](../update/README.md#checking-for-background-migrations-before-upgrading).
From version 12 onwards, an additional step is required. More significant migrations may occur during major release upgrades. To ensure these are successful, increment to the first minor version (`x.0.x`) during the major version jump. Then proceed with upgrading to a newer release. ### Version 12 onwards: Extra step for major upgrades
From version 12 onwards, an additional step is required. More significant migrations
may occur during major release upgrades.
To ensure these are successful:
1. Increment to the first minor version (`x.0.x`) during the major version jump.
1. Proceed with upgrading to a newer release.
For example: `11.11.x` -> `12.0.x` -> `12.8.x` For example: `11.11.x` -> `12.0.x` -> `12.8.x`
### Example upgrade paths
Please see the table below for some examples: Please see the table below for some examples:
| Latest stable version | Your version | Recommended upgrade path | Note | | Latest stable version | Your version | Recommended upgrade path | Note |
...@@ -155,8 +165,10 @@ Please see the table below for some examples: ...@@ -155,8 +165,10 @@ Please see the table below for some examples:
| 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` | | 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` |
| 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` | | 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` |
| 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` | | 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` |
| 12.5.8 | 11.3.4 | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.5.8` | `11.11.8` is the last version in version `11`. `12.0.x` [is a required step.](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23211#note_272842444) | | 12.5.8 | 11.3.4 | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.5.8` | `11.11.8` is the last version in version `11`. `12.0.x` [is a required step](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23211#note_272842444). |
| 12.8.5 | 9.2.6 | `9.2.6` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.8.5` | Four intermediate versions required: the final 9.5, 10.8, 11.11 releases, plus 12.0 | | 12.8.5 | 9.2.6 | `9.2.6` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.8.5` | Four intermediate versions are required: the final 9.5, 10.8, 11.11 releases, plus 12.0. |
## More information
More information about the release procedures can be found in our More information about the release procedures can be found in our
[release documentation](https://gitlab.com/gitlab-org/release/docs). You may also want to read our [release documentation](https://gitlab.com/gitlab-org/release/docs). You may also want to read our
... ...
......
...@@ -548,6 +548,7 @@ Supported applications: ...@@ -548,6 +548,7 @@ Supported applications:
- [Sentry](#install-sentry-using-gitlab-ci) - [Sentry](#install-sentry-using-gitlab-ci)
- [GitLab Runner](#install-gitlab-runner-using-gitlab-ci) - [GitLab Runner](#install-gitlab-runner-using-gitlab-ci)
- [Cilium](#install-cilium-using-gitlab-ci) - [Cilium](#install-cilium-using-gitlab-ci)
- [Vault](#install-vault-using-gitlab-ci)
- [JupyterHub](#install-jupyterhub-using-gitlab-ci) - [JupyterHub](#install-jupyterhub-using-gitlab-ci)
- [Elastic Stack](#install-elastic-stack-using-gitlab-ci) - [Elastic Stack](#install-elastic-stack-using-gitlab-ci)
- [Crossplane](#install-crossplane-using-gitlab-ci) - [Crossplane](#install-crossplane-using-gitlab-ci)
...@@ -813,6 +814,95 @@ agent: ...@@ -813,6 +814,95 @@ agent:
enabled: false enabled: false
``` ```
### Install Vault using GitLab CI
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/9982) in GitLab 12.9.
[Hashicorp Vault](https://vaultproject.io/) is a secrets management solution which
can be used to safely manage and store passwords, credentials, certificates and more. A Vault
installation could be leveraged to provide a single secure data store for credentials
used in your applications, GitLab CI jobs, and more. It could also serve as a way of
providing SSL/TLS certificates to systems and deployments in your infrastructure. Leveraging
Vault as a single source for all these credentials allows greater security by having
a single source of access, control, and auditability around all your sensitive
credentials and certificates.
To install Vault, enable it in the `.gitlab/managed-apps/config.yaml` file:
```yaml
vault:
installed: true
```
By default you will get a basic Vault setup with no high availability nor any scalable
storage backend. This is enough for simple testing and small scale deployments, though has limits
to how much it can scale, and as it is a single instance deployment, you will experience downtime
when upgrading the Vault application.
To optimally use Vault in a production environment, it's ideal to have a good understanding
of the internals of Vault and how to configure it. This can be done by reading the
[the Vault documentation](https://www.vaultproject.io/docs/internals/) as well as
the Vault Helm chart [values.yaml file](https://github.com/hashicorp/vault-helm/blob/v0.3.3/values.yaml).
At a minimum you will likely set up:
- A [seal](https://www.vaultproject.io/docs/configuration/seal/) for extra encryption
of the master key.
- A [storage backend](https://www.vaultproject.io/docs/configuration/storage/) that is
suitable for environment and storage security requirements.
- [HA Mode](https://www.vaultproject.io/docs/concepts/ha/).
- [The Vault UI](https://www.vaultproject.io/docs/configuration/ui/).
The following is an example values file (`.gitlab/managed-apps/vault/values.yaml`)
that configures Google Key Management Service for auto-unseal, using a Google Cloud Storage backend, enabling
the Vault UI, and enabling HA with 3 pod replicas. The `storage` and `seal` stanzas
below are examples and should be replaced with settings specific to your environment.
```yaml
# Enable the Vault WebUI
ui:
enabled: true
server:
# Disable the built in data storage volume as it's not safe for Hight Availablity mode
dataStorage:
enabled: false
# Enable High Availability Mode
ha:
enabled: true
# Configure Vault to listen on port 8200 for normal traffic and port 8201 for inter-cluster traffic
config: |
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
# Configure Vault to store its data in a GCS Bucket backend
storage "gcs" {
path = "gcs://my-vault-storage/vault-bucket"
ha_enabled = "true"
}
# Configure Vault to automatically unseal storage using a GKMS key
seal "gcpckms" {
project = "vault-helm-dev-246514"
region = "global"
key_ring = "vault-helm-unseal-kr"
crypto_key = "vault-helm-unseal-key"
}
```
Once you have successfully installed Vault, you will need to [initialize the Vault](https://learn.hashicorp.com/vault/getting-started/deploy#initializing-the-vault)
and obtain the initial root token. You will need access to your Kubernetes cluster that Vault has been deployed into in order to do this.
To initialise the Vault, get a shell to one of the Vault pods running inside Kubernetes (typically this is done by using the `kubectl` command line tool).
Once you have a shell into the pod, run the `vault operator init` command:
```shell
kubectl -n gitlab-managed-apps exec -it vault-0 sh
/ $ vault operator init
```
This should give you your unseal keys and initial root token. Make sure to note these down
and keep these safe as you will need them to unseal the Vault throughout its lifecycle.
### Install JupyterHub using GitLab CI ### Install JupyterHub using GitLab CI
> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/-/merge_requests/40) in GitLab 12.8. > [Introduced](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/-/merge_requests/40) in GitLab 12.8.
... ...
......
apply: apply:
stage: deploy stage: deploy
image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.11.0" image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.12.0"
environment: environment:
name: production name: production
variables: variables:
...@@ -16,6 +16,7 @@ apply: ...@@ -16,6 +16,7 @@ apply:
PROMETHEUS_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/prometheus/values.yaml PROMETHEUS_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/prometheus/values.yaml
ELASTIC_STACK_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/elastic-stack/values.yaml ELASTIC_STACK_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/elastic-stack/values.yaml
VAULT_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/vault/values.yaml VAULT_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/vault/values.yaml
CROSSPLANE_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/crossplane/values.yaml
script: script:
- gitlab-managed-apps /usr/local/share/gitlab-managed-apps/helmfile.yaml - gitlab-managed-apps /usr/local/share/gitlab-managed-apps/helmfile.yaml
only: only:
... ...
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
@client = client @client = client
end end
def pod_logs(namespace, pod_name, container_name: nil, search: nil, start_time: nil, end_time: nil, cursor: nil) def pod_logs(namespace, pod_name: nil, container_name: nil, search: nil, start_time: nil, end_time: nil, cursor: nil)
query = { bool: { must: [] } }.tap do |q| query = { bool: { must: [] } }.tap do |q|
filter_pod_name(q, pod_name) filter_pod_name(q, pod_name)
filter_namespace(q, namespace) filter_namespace(q, namespace)
...@@ -38,7 +38,7 @@ module Gitlab ...@@ -38,7 +38,7 @@ module Gitlab
{ "offset": { order: :desc } } { "offset": { order: :desc } }
], ],
# only return these fields in the response # only return these fields in the response
_source: ["@timestamp", "message"], _source: ["@timestamp", "message", "kubernetes.pod.name"],
# fixed limit for now, we should support paginated queries # fixed limit for now, we should support paginated queries
size: ::Gitlab::Elasticsearch::Logs::LOGS_LIMIT size: ::Gitlab::Elasticsearch::Logs::LOGS_LIMIT
} }
...@@ -51,6 +51,9 @@ module Gitlab ...@@ -51,6 +51,9 @@ module Gitlab
end end
def filter_pod_name(query, pod_name) def filter_pod_name(query, pod_name)
# We can filter by "all pods" with a null pod_name
return if pod_name.nil?
query[:bool][:must] << { query[:bool][:must] << {
match_phrase: { match_phrase: {
"kubernetes.pod.name" => { "kubernetes.pod.name" => {
...@@ -113,7 +116,8 @@ module Gitlab ...@@ -113,7 +116,8 @@ module Gitlab
results = results.map do |hit| results = results.map do |hit|
{ {
timestamp: hit["_source"]["@timestamp"], timestamp: hit["_source"]["@timestamp"],
message: hit["_source"]["message"] message: hit["_source"]["message"],
pod: hit["_source"]["kubernetes"]["pod"]["name"]
} }
end end
... ...
......
...@@ -711,9 +711,6 @@ msgstr "" ...@@ -711,9 +711,6 @@ msgstr ""
msgid "20-29 contributions" msgid "20-29 contributions"
msgstr "" msgstr ""
   
msgid "24 hours"
msgstr ""
msgid "2FA" msgid "2FA"
msgstr "" msgstr ""
   
...@@ -726,9 +723,6 @@ msgstr "" ...@@ -726,9 +723,6 @@ msgstr ""
msgid "3 hours" msgid "3 hours"
msgstr "" msgstr ""
   
msgid "30 days"
msgstr ""
msgid "30 minutes" msgid "30 minutes"
msgstr "" msgstr ""
   
...@@ -750,9 +744,6 @@ msgstr "" ...@@ -750,9 +744,6 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake." msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr "" msgstr ""
   
msgid "7 days"
msgstr ""
msgid "8 hours" msgid "8 hours"
msgstr "" msgstr ""
   
...@@ -7660,6 +7651,9 @@ msgstr "" ...@@ -7660,6 +7651,9 @@ msgstr ""
msgid "EnvironmentsDashboard|This dashboard displays a maximum of 7 projects and 3 environments per project. %{readMoreLink}" msgid "EnvironmentsDashboard|This dashboard displays a maximum of 7 projects and 3 environments per project. %{readMoreLink}"
msgstr "" msgstr ""
   
msgid "Environments|All pods"
msgstr ""
msgid "Environments|An error occurred while canceling the auto stop, please try again" msgid "Environments|An error occurred while canceling the auto stop, please try again"
msgstr "" msgstr ""
   
...@@ -7741,7 +7735,7 @@ msgstr "" ...@@ -7741,7 +7735,7 @@ msgstr ""
msgid "Environments|No deployments yet" msgid "Environments|No deployments yet"
msgstr "" msgstr ""
   
msgid "Environments|No pods to display" msgid "Environments|No pod selected"
msgstr "" msgstr ""
   
msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file." msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
... ...
......
...@@ -19,7 +19,12 @@ ...@@ -19,7 +19,12 @@
"_score": null, "_score": null,
"_source": { "_source": {
"message": "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13", "message": "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13",
"@timestamp": "2019-12-13T14:35:34.034Z" "@timestamp": "2019-12-13T14:35:34.034Z",
"kubernetes": {
"pod": {
"name": "production-6866bc8974-m4sk4"
}
}
}, },
"sort": [ "sort": [
9999998, 9999998,
...@@ -33,7 +38,12 @@ ...@@ -33,7 +38,12 @@
"_score": null, "_score": null,
"_source": { "_source": {
"message": "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13", "message": "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13",
"@timestamp": "2019-12-13T14:35:35.034Z" "@timestamp": "2019-12-13T14:35:35.034Z",
"kubernetes": {
"pod": {
"name": "production-6866bc8974-m4sk4"
}
}
}, },
"sort": [ "sort": [
9999949, 9999949,
...@@ -47,7 +57,12 @@ ...@@ -47,7 +57,12 @@
"_score": null, "_score": null,
"_source": { "_source": {
"message": "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13", "message": "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13",
"@timestamp": "2019-12-13T14:35:36.034Z" "@timestamp": "2019-12-13T14:35:36.034Z",
"kubernetes": {
"pod": {
"name": "production-6866bc8974-m4sk4"
}
}
}, },
"sort": [ "sort": [
9999944, 9999944,
...@@ -61,7 +76,12 @@ ...@@ -61,7 +76,12 @@
"_score": null, "_score": null,
"_source": { "_source": {
"message": "- -\u003e /", "message": "- -\u003e /",
"@timestamp": "2019-12-13T14:35:37.034Z" "@timestamp": "2019-12-13T14:35:37.034Z",
"kubernetes": {
"pod": {
"name": "production-6866bc8974-m4sk4"
}
}
}, },
"sort": [ "sort": [
9999934, 9999934,
... ...
......
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -40,7 +40,8 @@ ...@@ -40,7 +40,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -37,7 +37,8 @@ ...@@ -37,7 +37,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -42,7 +42,8 @@ ...@@ -42,7 +42,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -42,7 +42,8 @@ ...@@ -42,7 +42,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -42,7 +42,8 @@ ...@@ -42,7 +42,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -43,7 +43,8 @@ ...@@ -43,7 +43,8 @@
], ],
"_source": [ "_source": [
"@timestamp", "@timestamp",
"message" "message",
"kubernetes.pod.name"
], ],
"size": 500 "size": 500
} }
...@@ -300,9 +300,10 @@ describe('EnvironmentLogs', () => { ...@@ -300,9 +300,10 @@ describe('EnvironmentLogs', () => {
const items = findPodsDropdown().findAll(GlDropdownItem); const items = findPodsDropdown().findAll(GlDropdownItem);
expect(findPodsDropdown().props('text')).toBe(mockPodName); expect(findPodsDropdown().props('text')).toBe(mockPodName);
expect(items.length).toBe(mockPods.length); expect(items.length).toBe(mockPods.length + 1);
expect(items.at(0).text()).toBe('All pods');
mockPods.forEach((pod, i) => { mockPods.forEach((pod, i) => {
const item = items.at(i); const item = items.at(i + 1);
expect(item.text()).toBe(pod); expect(item.text()).toBe(pod);
}); });
}); });
...@@ -345,7 +346,7 @@ describe('EnvironmentLogs', () => { ...@@ -345,7 +346,7 @@ describe('EnvironmentLogs', () => {
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything()); expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
items.at(index).vm.$emit('click'); items.at(index + 1).vm.$emit('click');
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]); expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
}); });
... ...
......
...@@ -32,15 +32,93 @@ export const mockPods = [ ...@@ -32,15 +32,93 @@ export const mockPods = [
]; ];
export const mockLogsResult = [ export const mockLogsResult = [
{ timestamp: '2019-12-13T13:43:18.2760123Z', message: 'Log 1' }, {
{ timestamp: '2019-12-13T13:43:18.2760123Z', message: 'Log 2' }, timestamp: '2019-12-13T13:43:18.2760123Z',
{ timestamp: '2019-12-13T13:43:26.8420123Z', message: 'Log 3' }, message: '10.36.0.1 - - [16/Oct/2019:06:29:48 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:18.2760123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:26.8420123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:29:57 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:26.8420123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:28.3710123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:29:58 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:28.3710123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:36.8860123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:07 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:36.8860123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:38.4000123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:08 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:38.4000123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:46.8420123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:17 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:46.8430123Z',
message: '- -> /',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:48.3240123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:18 UTC] "GET / HTTP/1.1" 200 13',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:48.3250123Z',
message: '- -> /',
pod: 'bar',
},
]; ];
export const mockTrace = [ export const mockTrace = [
'Dec 13 13:43:18.276Z | Log 1', 'Dec 13 13:43:18.276Z | foo | 10.36.0.1 - - [16/Oct/2019:06:29:48 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:18.276Z | Log 2', 'Dec 13 13:43:18.276Z | bar | - -> /',
'Dec 13 13:43:26.842Z | Log 3', 'Dec 13 13:43:26.842Z | foo | 10.36.0.1 - - [16/Oct/2019:06:29:57 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:26.842Z | bar | - -> /',
'Dec 13 13:43:28.371Z | foo | 10.36.0.1 - - [16/Oct/2019:06:29:58 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:28.371Z | bar | - -> /',
'Dec 13 13:43:36.886Z | foo | 10.36.0.1 - - [16/Oct/2019:06:30:07 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:36.886Z | bar | - -> /',
'Dec 13 13:43:38.400Z | foo | 10.36.0.1 - - [16/Oct/2019:06:30:08 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:38.400Z | bar | - -> /',
'Dec 13 13:43:46.842Z | foo | 10.36.0.1 - - [16/Oct/2019:06:30:17 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:46.843Z | bar | - -> /',
'Dec 13 13:43:48.324Z | foo | 10.36.0.1 - - [16/Oct/2019:06:30:18 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:48.325Z | bar | - -> /',
]; ];
export const mockResponse = { export const mockResponse = {
... ...
......
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
fetchMoreLogsPrepend, fetchMoreLogsPrepend,
} from '~/logs/stores/actions'; } from '~/logs/stores/actions';
import { defaultTimeRange } from '~/monitoring/constants'; import { defaultTimeRange } from '~/vue_shared/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import flash from '~/flash'; import flash from '~/flash';
...@@ -172,14 +172,13 @@ describe('Logs Store actions', () => { ...@@ -172,14 +172,13 @@ describe('Logs Store actions', () => {
describe('fetchLogs', () => { describe('fetchLogs', () => {
beforeEach(() => { beforeEach(() => {
expectedMutations = [ expectedMutations = [
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA }, { type: types.REQUEST_LOGS_DATA },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ {
type: types.RECEIVE_LOGS_DATA_SUCCESS, type: types.RECEIVE_LOGS_DATA_SUCCESS,
payload: { logs: mockLogsResult, cursor: mockNextCursor }, payload: { logs: mockLogsResult, cursor: mockNextCursor },
}, },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
]; ];
expectedActions = []; expectedActions = [];
...@@ -364,7 +363,6 @@ describe('Logs Store actions', () => { ...@@ -364,7 +363,6 @@ describe('Logs Store actions', () => {
null, null,
state, state,
[ [
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA }, { type: types.REQUEST_LOGS_DATA },
{ type: types.RECEIVE_PODS_DATA_ERROR }, { type: types.RECEIVE_PODS_DATA_ERROR },
{ type: types.RECEIVE_LOGS_DATA_ERROR }, { type: types.RECEIVE_LOGS_DATA_ERROR },
... ...
......
...@@ -223,17 +223,6 @@ describe('Logs Store Mutations', () => { ...@@ -223,17 +223,6 @@ describe('Logs Store Mutations', () => {
}); });
}); });
describe('REQUEST_PODS_DATA', () => {
it('receives pods data', () => {
mutations[types.REQUEST_PODS_DATA](state);
expect(state.pods).toEqual(
expect.objectContaining({
options: [],
}),
);
});
});
describe('RECEIVE_PODS_DATA_SUCCESS', () => { describe('RECEIVE_PODS_DATA_SUCCESS', () => {
it('receives pods data success', () => { it('receives pods data success', () => {
mutations[types.RECEIVE_PODS_DATA_SUCCESS](state, mockPods); mutations[types.RECEIVE_PODS_DATA_SUCCESS](state, mockPods);
... ...
......
...@@ -78,6 +78,7 @@ exports[`Dashboard template matches the default snapshot 1`] = ` ...@@ -78,6 +78,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
label-size="sm" label-size="sm"
> >
<date-time-picker-stub <date-time-picker-stub
customenabled="true"
options="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]" options="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
value="[object Object]" value="[object Object]"
/> />
... ...
......
...@@ -7,7 +7,7 @@ import { mockProjectDir } from '../mock_data'; ...@@ -7,7 +7,7 @@ import { mockProjectDir } from '../mock_data';
import Dashboard from '~/monitoring/components/dashboard.vue'; import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import { defaultTimeRange } from '~/monitoring/constants'; import { defaultTimeRange } from '~/vue_shared/constants';
import { propsData } from '../init_utils'; import { propsData } from '../init_utils';
jest.mock('~/flash'); jest.mock('~/flash');
... ...
......