├── tests ├── inventory └── test.yml ├── files └── ipfs-cluster.service ├── handlers └── main.yaml ├── defaults └── main.yml ├── meta └── main.yml ├── .travis.yml ├── LICENSE ├── templates └── service.json ├── tasks └── main.yml └── README.md /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /files/ipfs-cluster.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=IPFS Cluster Service 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/local/bin/ipfs-cluster-service 7 | Restart=on-failure 8 | User=ipfs 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd 2 | become: yes 3 | systemd: daemon_reload=yes 4 | notify: restart IPFS Cluster 5 | 6 | - name: restart IPFS Cluster 7 | become: yes 8 | service: 9 | name: ipfs-cluster 10 | enabled: yes 11 | state: restarted 12 | use: systemd 13 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ipfs_cluster_dist_url: https://dist.ipfs.io 3 | ipfs_cluster_version: v0.2.1 4 | ipfs_cluster_arch: amd64 5 | ipfs_cluster_replication_factor: -1 # replicate files to all nodes 6 | ipfs_cluster_listen_multiaddress: "/ip4/{{ ansible_default_ipv4.address }}/tcp/9096" 7 | ipfs_cluster_http_api_listen_multiaddress: /ip4/127.0.0.1/tcp/9094 8 | ipfs_cluster_http_gateway_listen_multiaddress: /ip4/127.0.0.1/tcp/9095 9 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Ben Jeffrey 3 | description: Collective pinning and composition for IPFS. 4 | license: MIT 5 | min_ansible_version: 2.2 6 | platforms: 7 | - name: Ubuntu 8 | versions: 9 | - xenial 10 | galaxy_tags: 11 | - ipfs 12 | - ipfs-cluster 13 | - daemon 14 | - networking 15 | - distributed 16 | - p2p 17 | - file 18 | - system 19 | dependencies: 20 | - role: jeffbr13.ipfs 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | sudo: required 6 | dist: xenial 7 | 8 | install: 9 | - sudo apt-get install -y software-properties-common python-software-properties 10 | - sudo add-apt-repository -y ppa:ansible/ansible 11 | - sudo apt-get update -y 12 | - sudo apt-get install -y ansible 13 | # Check ansible version 14 | - ansible --version 15 | # Create ansible.cfg with correct roles_path 16 | - 'printf "[defaults]\nroles_path=../" > ansible.cfg' 17 | - 'printf "\nallow_world_readable_tmpfiles=True" >> ansible.cfg' 18 | - ansible-galaxy install jeffbr13.ipfs 19 | 20 | env: 21 | - ANSIBLE_HOST_KEY_CHECKING=False 22 | 23 | script: 24 | - ansible-playbook -i tests/inventory tests/test.yml --syntax-check 25 | - ansible-playbook tests/test.yml -v 26 | 27 | notifications: 28 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hector Sanjuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - ansible-ipfs-cluster 6 | vars: 7 | ipfs_peer_id: QmeZJEi1bQ8qWQ6jTS1sQC1WkDYQdPUPiQnj5tAF2MteSP 8 | ipfs_private_key: "CAASpgkwggSiAgEAAoIBAQCrUFdYnKoOHP0E4O4f8fe9yTG3ExKpntJqkAkjEcr8m2pkKUNMecog6mIEqUZWrtJhvW+9sh1v1CULDUtA4lt66e6EP63Ux41S6ynrQWt8ToM1uZqU78kyWscTk5oNfVtQx36lu+tMxfZz+djagxOLsRFvcmOsZoaawS2YbqlC4jo/KrN8sqSJrJshmTub6ObiJkOSWgowgHo1qy27qNpL7OCPG5w5TOm/j0572khSrc4KBqlqCUX2i3HMMGpGWBy0iPFKejgJva5z5+MHmOMG24k4ObOFDhOQ8AQnxjme8BoUYWrDdYx+KjcS6P130LgSPpsH/eSzrjKSSPU550xRAgMBAAECggEAA0HNx1ODMC2A6fzUyiqxiRwwI8jrC+qHGZuPcOk/sLnLtgfKzcNrAmEyZ+BIBBHi0hTl8PdX8+5WnfUsvxkOoDGeZUlIpsj8eN0buOtPfdeISxcyRy6f5qMvMcIWpbcCa3C/iase+CCzZNUVOGJRltBRAotsNbTlTj/p/AHumeTnO6a/nigWuMalIUYOqq/XF/XJqVaz+yznSW58iZjcvlVqMekLwMI18yj/jbjAz2i4MSg6X4jiKnxH7ZWqP+5JRZi3TRdY6VuEJA8C7+H8DnBTsqWB6bM2BG2XbaIOqj3p2592EOtXeJ9Cuk69Y0euAzhBRsnOciWNA+3VPSGXgQKBgQDVf+RbIpZxRPYgQZHn3jVOwLCUMn9/hSfqBiDj28mI3XMgErvf97slauRP1rDVYzUyVzoaikAZOIMSjc3j8tt+s331pmUp7yH2YMyeRF+euhRVbu0PUBErNnrM/d5LQD8WXykBC7aJqbDUcwl8FArqQRCN81aXCtxsot5xgw+MyQKBgQDNaqFOtwmHVAv4OIrVBr2AVrKUBcnVexZPmmy1hFyTwQl2qOijpDJKAeEWvcD3oKwN9Uft2CGVPo6oANTLCC3ipqTkQia5T4dGv91tQmsAAETqCFUn3NI7BXqWPo6zjC8lFlYQD2SHkBym7NLUY/bZI07ShDMr+VwDRLZLOIVvSQKBgDZU86IVrT9qE1CmHyFotRwzgTxVlYi4NC4kHR6fgiwoXvHckp8uhhvBlhSTSDElrBYBiLHUWPeSIBkKqUjHNwCJYRawn7IKPxRaEfh84fCc3ivXrNBmyde7Z4jfWkDAOs3QrbisaGmkG3IVTGadGyH+KN5W/MQ3aVzkHcO5XK2xAoGACfObgvu9G3BH8CuVFkLwVVJd7mvlXlFLQGoD0DtsXMAiXDxMoPjocstqgzaILbb7/uUeKfipj9hrAtqfdIKd7ngSTvihdaLTs+rBqHRqSKK6s/fZYEgDa1aVnMfFFA+7jPlrC/Yi8apaqP+aKy34nIZoYCyFaiD3gzUF+9juJfkCgYBWfBGSC2WeuPqJwe8Ai9bMTd7USY4zJHGYUt3l1Wpmi/cS0WJOCrOEeqj+5or6Mv0XDd854Dbqq1fgu5WSf5mljsBOCGMyA7NkflsXRpP9R9B5AuPNFaOzUpBkCNm3MyKeykrQNrwYo+dx7s3yxSBReTMYTgw2041ZsNRbxnmwbA==" 9 | ipfs_cluster_secret: 6ac92d72f737fbc206a3fb55c55b680798359162bc84831df6167feba858e93d 10 | -------------------------------------------------------------------------------- /templates/service.json: -------------------------------------------------------------------------------- 1 | { 2 | "cluster": { 3 | "secret": "{{ ipfs_cluster_secret }}", 4 | "id": "{{ ipfs_peer_id }}", 5 | "private_key": "{{ ipfs_private_key }}", 6 | "peers": [ 7 | {% for host in ansible_play_hosts %} 8 | "{{ hostvars[host]['ipfs_cluster_listen_multiaddress'] }}/ipfs/{{ hostvars[host]['ipfs_peer_id'] }}"{% if not loop.last %},{% endif %} 9 | 10 | {% endfor %} 11 | ], 12 | "bootstrap": [], 13 | "leave_on_shutdown": false, 14 | "listen_multiaddress": "{{ ipfs_cluster_listen_multiaddress }}", 15 | "state_sync_interval": "1m0s", 16 | "ipfs_sync_interval": "2m10s", 17 | "replication_factor": {{ ipfs_cluster_replication_factor }}, 18 | "monitor_ping_interval": "15s" 19 | }, 20 | "consensus": { 21 | "raft": { 22 | "heartbeat_timeout": "1s", 23 | "election_timeout": "1s", 24 | "commit_timeout": "50ms", 25 | "max_append_entries": 64, 26 | "trailing_logs": 10240, 27 | "snapshot_interval": "2m0s", 28 | "snapshot_threshold": 8192, 29 | "leader_lease_timeout": "500ms" 30 | } 31 | }, 32 | "api": { 33 | "restapi": { 34 | "listen_multiaddress": "{{ ipfs_cluster_http_api_listen_multiaddress}}", 35 | "read_timeout": "30s", 36 | "read_header_timeout": "5s", 37 | "write_timeout": "1m0s", 38 | "idle_timeout": "2m0s", 39 | "basic_auth_credentials": null 40 | } 41 | }, 42 | "ipfs_connector": { 43 | "ipfshttp": { 44 | "proxy_listen_multiaddress": "{{ ipfs_cluster_http_gateway_listen_multiaddress }}", 45 | "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", 46 | "connect_swarms_delay": "7s", 47 | "proxy_read_timeout": "10m0s", 48 | "proxy_read_header_timeout": "5s", 49 | "proxy_write_timeout": "10m0s", 50 | "proxy_idle_timeout": "1m0s" 51 | } 52 | }, 53 | "monitor": { 54 | "monbasic": { 55 | "check_interval": "15s" 56 | } 57 | }, 58 | "informer": { 59 | "disk": { 60 | "metric_ttl": "30s", 61 | "metric_type": "freespace" 62 | }, 63 | "numpin": { 64 | "metric_ttl": "10s" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create download folder for ipfs-cluster 3 | become: yes 4 | file: 5 | state: directory 6 | owner: root 7 | group: root 8 | dest: /opt/ipfs-cluster/{{ipfs_cluster_version}} 9 | 10 | - name: download and unpack IPFS cluster 11 | become: yes 12 | unarchive: 13 | remote_src: yes 14 | src: "{{ ipfs_cluster_dist_url }}/{{ item }}/{{ipfs_cluster_version}}/{{ item }}_{{ipfs_cluster_version}}_linux-{{ipfs_cluster_arch}}.tar.gz" 15 | dest: /opt/ipfs-cluster/{{ipfs_cluster_version}} 16 | creates: /opt/ipfs-cluster/{{ipfs_cluster_version}}/{{ item }} 17 | with_items: 18 | - ipfs-cluster-service 19 | - ipfs-cluster-ctl 20 | 21 | - name: link ipfs cluster executables 22 | become: yes 23 | file: 24 | state: link 25 | owner: root 26 | group: root 27 | dest: /usr/local/bin/{{ item }} 28 | src: /opt/ipfs-cluster/{{ipfs_cluster_version}}/{{ item }}/{{ item }} 29 | with_items: 30 | - ipfs-cluster-service 31 | - ipfs-cluster-ctl 32 | 33 | - name: install ipfs-cluster init service 34 | become: yes 35 | copy: 36 | src: ipfs-cluster.service 37 | dest: /etc/systemd/system/ipfs-cluster.service 38 | owner: root 39 | group: root 40 | mode: 0644 41 | notify: 42 | - reload systemd 43 | - restart IPFS Cluster 44 | 45 | - name: make .ipfs-cluster directory 46 | become: yes 47 | become_user: ipfs 48 | file: 49 | state: directory 50 | mode: 0700 51 | owner: ipfs 52 | group: ipfs 53 | dest: /home/ipfs/.ipfs-cluster 54 | 55 | - name: copy configuration 56 | become: yes 57 | become_user: ipfs 58 | template: 59 | src: service.json 60 | dest: /home/ipfs/.ipfs-cluster/service.json 61 | mode: 0600 62 | owner: ipfs 63 | group: ipfs 64 | notify: restart IPFS Cluster 65 | register: ipfs_cluster_configuration 66 | 67 | - name: stop ipfs-cluster on configuration change 68 | become: yes 69 | service: 70 | name: ipfs-cluster 71 | state: stopped 72 | use: systemd 73 | when: ipfs_cluster_configuration.changed 74 | 75 | - name: destroy ipfs-cluster state on configuration change 76 | become: yes 77 | file: 78 | path: /home/ipfs/.ipfs-cluster/ipfs-cluster-data 79 | state: absent 80 | when: ipfs_cluster_configuration.changed 81 | 82 | - name: reload systemd 83 | become: yes 84 | systemd: 85 | daemon_reload: yes 86 | 87 | - name: enable and start IPFS Cluster 88 | become: yes 89 | service: 90 | name: ipfs-cluster 91 | state: started 92 | enabled: yes 93 | use: systemd 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jeffbr13.ipfs-cluster 2 | ===================== 3 | 4 | Installs and configures the [`ipfs-cluster`](https://github.com/ipfs/ipfs-cluster) daemon. 5 | 6 | Unique node IDs and private keys must be configured per-host. 7 | 8 | You can use `systemctl status ipfs` and `systemctl status ipfs-cluster` to check the status of the new services. 9 | 10 | 11 | Requirements 12 | ------------ 13 | 14 | - Ansible 2.2 (for service.use option) 15 | 16 | 17 | Role Variables 18 | -------------- 19 | 20 | ### Required Variables 21 | 22 | As in [jeffbr13.ipfs](https://galaxy.ansible.com/jeffbr13/ipfs), 23 | the IPFS peer ID and private key must be set for each host in `host_vars`: 24 | 25 | ```yaml 26 | ipfs_peer_id: "" # see below 27 | ipfs_private_key: "" # ipfs-key | base64 28 | ``` 29 | 30 | You must also generate an `ipfs_cluster_secret` to be shared by all hosts: 31 | 32 | ```yaml 33 | ipfs_cluster_secret: "" # openssl rand -hex 32 34 | ``` 35 | 36 | ### Optional Variables 37 | 38 | The following variables are available across the playbook: 39 | 40 | ```yaml 41 | ipfs_cluster_dist_url: https://dist.ipfs.io 42 | ipfs_cluster_version: v0.2.1 43 | ipfs_cluster_arch: amd64 44 | ipfs_cluster_replication_factor: -1 # replicate files to all nodes 45 | ipfs_cluster_listen_multiaddress: "/ip4/{{ ansible_default_ipv4.address }}/tcp/9096" 46 | ipfs_cluster_http_api_listen_multiaddress: /ip4/127.0.0.1/tcp/9094 47 | ipfs_cluster_http_gateway_listen_multiaddress: /ip4/127.0.0.1/tcp/9095 48 | ``` 49 | 50 | You can generate the `ipfs_cluster_secret` by running `openssl rand -hex 32`, 51 | or, if you don't have OpenSSL, `od -vN 32 -An -tx1 /dev/urandom | tr -d ' \n' ; echo`. 52 | 53 | ipfs-cluster listens on the machine's default IPv4 address (`ansible_default_ipv4`) as ascertained by Ansible. 54 | To customise this, e.g. for a private network, override `ipfs_cluster_listen_multiaddress`. 55 | 56 | 57 | Example Playbook 58 | ---------------- 59 | 60 | In your `playbook.yml`: 61 | 62 | ```yaml 63 | - hosts: ipfs_cluster 64 | roles: 65 | - { role: jeffbr13.ipfs-cluster, ipfs_cluster_secret: ed3205fca12ec1584d063f9f16a7b72220f86e49e5c8e94625c0aace6b37d713} 66 | ``` 67 | 68 | For a host in `host_vars/.yml`: 69 | 70 | ```yaml 71 | ipfs_peer_id: Qmagy4195bsbR6YmqwKUtj7j65QdnyDiH3uzVYB8F46r6j 72 | ipfs_private_key: CAASqAkwggSkAgEAAoIBAQDB7ltlNV4QysC3m0HOySU2jjxnmrjZmOX8vi9UU/E6xu+3S6wCe/f1bxVATgqfHMXU4Sk5g4pmM3YocadrMe/WzeSLKGB0QPgcKAtcuBJZO8vQEWmMDxrFg/pOd4Z2LIrLKIQDeRmhBhMmIWQ/M8FNVhzSv6gVs0+X72ZwMQgJ8N5IiJBtzfT0CRddsHaSqiwvRPS2bJSuSUi0Vq1l4+I6sHyP7WgE9IrUQSMVHw3kiKgk8bWKEbW8p5GWXbdYPOfGr+YUd3GtEVvkrpo0OAZcA6H+1DqNAqAQdLu47pA7pzZW4C3nxwisN1wYoEbkE+qXpJQft4+58N1ZAkULbzu/AgMBAAECggEAAnTAV5HLdS78LdcbiEDn5b77aNx+xtK25vKJqum9Pl9SneGpdgaX51XW0Q+r9sPohX+sg/v0fsLcFjsKQcNKJFBLOq/yOMax3blsG2qBYPvu4t21ln6Ceknnm6LL4ydBQr1qnpikCHQJPgxiNqKzKgWTK+AdgtjYgzYW+AjG70lF+t450PtEp2NSPUsYB3EIUTRaPpQeKIQ3z71DkpjReuQ//P+PLLj7/4qzyHH1hkhHP6AgjwCetzH+Tlor0rigpkqY1QTycPmEXoQ5oekEIpGiVaX4LtymHWSIHW/FFEb5dODECwQtLRMulJpu4y86DfAySb9jwfn2JA9NQh5boQKBgQDI7a7DQBP6oJtfmwRhxonMO2L0czTIgZEl32E3JWx49ZY+xnAIvpUB3MZZqAX4vrb8uuvpv2ced4eeSyBAFJHOX8pEpEIfmgV7eg42xMPWr6l4DeXY3sC1SaXUWIzoSQLosD8kBopQNFemskmq5m5/eAdDxLZFa4K27BmmITf9hwKBgQD3FbK6TNoq2yWoxByPY4xmqkTeqh+DME3vBzXUDv8ZKqkVTkF7kFkk3zSsFFk/BS+UALsZKyarERbZ5U9NCscNZeVIukztjJUGCPpntjGM8r8NK3CXtWc+qGHewAgNGXzn6cyNVv29olPJNK0gvDkVou4NwEVMWjzDuFaiLZKeCQKBgQC0Mm1UUCha0iTmBilU4vB8CBqD7ro8w+5/j6kpAtgYVu/q1p5tSTZrWCtPBuBsJ+YGHEEs/eomKb6n2OpQbeIhuki1bLacjs4x4dHTjn2wERQkRhqHd6ZOL4GYQd4FCE2ij0XhMjhjG74sEqL8sPISQXwKa+WntnahRHbwRcRoCwKBgQCERqR5Ih2F5e5iTCLyDJwkdjEKd08Jf3mpZlXF4gVlZrZARrW9vchLegcLvJUOrOsMs9t2HOjFmg9+tUlf+E4Z+RvndH0six9YrMPJc/tQ9r+bAE91mFLec2x5wJpO0P9SdJLic9jBhb6PL9kjdkClOaVxzSYMOx7etLgEeJtOaQKBgAVYOsf2EA3aoukh6mPTKVfKaV71mFRhIPcbT0mADaJ7mYh0fSV/FHahwRioKfweTFNEFVO15x97acBbfInA/ZARc+N08IKVfwr+vopcefXEqgE0PMGsI/fFduXYBbprdns62qGrEYAJkNE8E34AM2Afh9sWt9PJdUYpBagHc+dL 73 | ``` 74 | 75 | 76 | License 77 | ------- 78 | 79 | MIT 80 | 81 | 82 | Author Information 83 | ------------------ 84 | 85 | This is an Ansible Galaxy distributable role based on . 86 | --------------------------------------------------------------------------------