ansible / ansible

Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management, in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com.
https://www.ansible.com/
GNU General Public License v3.0
63.11k stars 23.93k forks source link

k8s_raw module does not allow multiple Kubernetes objects in one file #40684

Closed geerlingguy closed 6 years ago

geerlingguy commented 6 years ago
SUMMARY

When using the k8s_raw module, if you feed it a document with multiple Kubernetes objects (e.g. a Service and a Deployment, separated by the YAML document separator ---), then the module will error out with Error loading resource_definition: expected a single document in the stream.

In the Kubernetes Configuration Best Practices docs, it's recommended to group all related objects in one file if possible:

Group related objects into a single file whenever it makes sense. One file is often easier to manage than several. See the guestbook-all-in-one.yaml file as an example of this syntax.

The k8s_raw module should support deploying definition files with more than one object.

ISSUE TYPE
COMPONENT NAME

k8s_raw

ANSIBLE VERSION
ansible 2.5.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/Users/jeff.geerling/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.14 (default, Mar 22 2018, 14:43:05) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
CONFIGURATION

macOS 10.13.4 High Sierra - host Raspbian 'Stretch' Lite - managed server

STEPS TO REPRODUCE

Kubernetes definition file, files/drupal8.yml:

# Drupal 8 Service definition.
apiVersion: v1
kind: Service
metadata:
  name: drupal8
  labels:
    app: drupal8
spec:
  ports:
    - port: 80
  selector:
    app: drupal8
    tier: frontend
  type: NodePort
---
# Drupal 8 Deployment definition.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: drupal8
  labels:
    app: drupal8
spec:
  selector:
    matchLabels:
      app: drupal8
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: drupal8
        tier: frontend
    spec:
      containers:
        - image: drupal:8
          name: drupal8
          env:
            - name: DRUPAL_DB_HOST
              value: drupal8-mysql
            - name: DRUPAL_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: drupal8-mysql-pass
                  key: password
          ports:
            - containerPort: 80
              name: drupal8

Playbook:

- hosts: kubernetes
  become: yes

  tasks:
    ...
    - name: Deploy Drupal 8 K8s service to the cluster.
      k8s_raw:
        definition: "{{ lookup('file', 'files/drupal8.yml') | from_yaml }}"
        state: present
    ...
EXPECTED RESULTS

The k8s_raw module should apply all the definitions in the file, just like running kubectl -f drupal8.yml would do on the cluster itself using kubectl.

ACTUAL RESULTS

The module fails with the following message:

TASK [Deploy Drupal 8 Kubernetes services to the cluster.] *************************************************************
failed: [10.0.100.44] (item=drupal8.yml) => changed=false 
  item: drupal8.yml
  msg: |-
    Error loading resource_definition: expected a single document in the stream
      in "/root/drupal8.yml", line 2, column 1
    but found another document
      in "/root/drupal8.yml", line 15, column 1

The same failure happens if I:

  1. Copy the definition files to the server.
  2. Run the task using src instead of definition and a lookup (e.g. src: "path/to/drupal8.yml").
ansibot commented 6 years ago

Files identified in the description:

If these files are inaccurate, please update the component name section of the description or use the !component bot command.

click here for bot help

ansibot commented 6 years ago

cc @chouseknecht @fabianvf @flaper87 @maxamillion @samdoran click here for bot help

geerlingguy commented 6 years ago

Deprecated?

Workaround I'm using for now is:

- name: Copy Kubernetes definition files to the cluster.
  copy:
    src: "files/{{ item }}"
    dest: "~/{{ item }}"
  with_items:
    - drupal8.yml
    - ...

- name: Apply Drupal 8 Kubernetes services to the cluster.
  command: kubectl apply -f ~/{{ item }}
  with_items:
    - drupal8.yml
    - ...
  register: drupal8_result
  changed_when: "'created' in drupal8_result.stdout"
  run_once: True
fabianvf commented 6 years ago

In Ansible 2.6 the k8s_raw (now k8s) module had a significant rewrite, it will support multiple documents in a file passed with src. I think the template lookup method still fails (looks like failure is from from_yaml, not the module), I'll look into allowing it as a string and working around it that way.

geerlingguy commented 6 years ago

@fabianvf - Ah, that explains it!

Hopefully we can stabilize on k8s for the foreseeable future ;)

I think the YAML library doesn't support reading in the file with multiple documents... but that's based on some fuzzy memory from searching around over the past day, so could be incorrect. If we can use it as a string that would be ideal (IMHO).

fabianvf commented 6 years ago

@geerlingguy yeah, some people interpreted _raw as "internal and dangerous", so we figured we'd make it more clear. k8s_raw and openshift_raw will still work, they're just aliased to k8s. I'll try to get a PR in tomorrow for the string thing.

geerlingguy commented 6 years ago

See related: https://github.com/ansible/ansible/issues/42476 — you still can't read a file in directly from the Ansible control machine with multiple definitions and feed it to the k8s module; you still get an error like:

ComposerError: expected a single document in the stream
  in "<unicode string>", line 2, column 1:
    apiVersion: v1
    ^
but found another document
  in "<unicode string>", line 7, column 1:
    ---
    ^

fatal: [master]: FAILED! => {
    "msg": "Unexpected failure during module execution.", 
    "stdout": ""
}

Therefore, Ansible 2.7 will introduce a new filter, from_yaml_all, which can be used like:

- k8s:
    src: "{{ item }}"
  loop: "{{ lookup('template', 'path/to/template.yml') | from_yaml_all }}"

(At least I think.) For now, I'm just copying all the files with multiple definitions to the kubernetes master and running them there instead of parsing/passing the definition(s). It's just easier that way :(