mirror of
https://github.com/ipfs/kubo.git
synced 2026-03-02 23:08:07 +08:00
Merge branch 'master' into release
This commit is contained in:
commit
fd6b59bb6e
@ -6,16 +6,21 @@ aliases:
|
||||
restore_gomod: &restore_gomod
|
||||
restore_cache:
|
||||
keys:
|
||||
- v4-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-{{ .Environment.CIRCLE_JOB }}
|
||||
- v4-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-
|
||||
- v4-dep-{{ .Branch }}-
|
||||
- v4-dep-master-
|
||||
- v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-{{ .Environment.CIRCLE_JOB }}
|
||||
- v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-
|
||||
- v5-dep-{{ .Branch }}-
|
||||
- v5-dep-master-
|
||||
store_gomod: &store_gomod
|
||||
save_cache:
|
||||
key: v4-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-{{ .Environment.CIRCLE_JOB }}
|
||||
key: v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/go-ipfs/go.sum" }}-{{ .Environment.CIRCLE_JOB }}
|
||||
paths:
|
||||
- ~/go/pkg/mod
|
||||
- ~/.cache/go-build/
|
||||
only-version-tags: &only-version-tags
|
||||
tags:
|
||||
only: /^v[0-9].*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
default_environment: &default_environment
|
||||
SERVICE: circle-ci
|
||||
@ -27,12 +32,8 @@ default_environment: &default_environment
|
||||
|
||||
executors:
|
||||
golang:
|
||||
parameters:
|
||||
tag:
|
||||
type: string
|
||||
default: "1.12"
|
||||
docker:
|
||||
- image: circleci/golang:<< parameters.tag >>
|
||||
- image: circleci/golang:1.13.8
|
||||
working_directory: ~/ipfs/go-ipfs
|
||||
environment:
|
||||
<<: *default_environment
|
||||
@ -57,6 +58,12 @@ executors:
|
||||
IPFS_REUSEPORT: false
|
||||
LIBP2P_ALLOW_WEAK_RSA_KEYS: 1
|
||||
E2E_IPFSD_TYPE: go
|
||||
dockerizer:
|
||||
docker:
|
||||
- image: circleci/golang:1.13.8
|
||||
environment:
|
||||
IMAGE_NAME: ipfs/go-ipfs
|
||||
WIP_IMAGE_TAG: wip
|
||||
|
||||
jobs:
|
||||
gobuild:
|
||||
@ -170,22 +177,17 @@ jobs:
|
||||
git -C interop log -1
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-interop-{{ checksum "~/ipfs/go-ipfs/interop/package-lock.json" }}
|
||||
- v1-interop-
|
||||
- v2-interop-{{ checksum "~/ipfs/go-ipfs/interop/package-lock.json" }}
|
||||
- v2-interop-
|
||||
- run:
|
||||
name: Installing dependencies
|
||||
command: |
|
||||
npm install
|
||||
working_directory: ~/ipfs/go-ipfs/interop
|
||||
- save_cache:
|
||||
key: v1-interop-{{ checksum "~/ipfs/go-ipfs/interop/package-lock.json" }}
|
||||
key: v2-interop-{{ checksum "~/ipfs/go-ipfs/interop/package-lock.json" }}
|
||||
paths:
|
||||
- ~/ipfs/go-ipfs/interop/node_modules
|
||||
- run:
|
||||
name: Installing js-ipfs
|
||||
command: |
|
||||
npm install ipfs ipfs-http-client
|
||||
working_directory: ~/ipfs/go-ipfs/interop
|
||||
- run:
|
||||
name: Installing reporting tools
|
||||
command: |
|
||||
@ -207,9 +209,7 @@ jobs:
|
||||
- store_test_results:
|
||||
path: /tmp/test-results
|
||||
go-ipfs-api:
|
||||
executor:
|
||||
name: golang
|
||||
tag: "1.13"
|
||||
executor: golang
|
||||
steps:
|
||||
- *make_out_dirs
|
||||
- attach_workspace:
|
||||
@ -306,8 +306,45 @@ jobs:
|
||||
key: v1-ipfs-webui-{{ checksum "~/ipfs/go-ipfs/ipfs-webui/package-lock.json" }}
|
||||
paths:
|
||||
- ~/ipfs/go-ipfs/ipfs-webui/node_modules
|
||||
docker-build:
|
||||
executor: dockerizer
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: "18.09.3"
|
||||
- run:
|
||||
name: Build Docker image
|
||||
command: |
|
||||
docker build -t $IMAGE_NAME:$WIP_IMAGE_TAG .
|
||||
- run:
|
||||
name: Archive Docker image
|
||||
command: docker save -o go-ipfs-image.tar $IMAGE_NAME
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./go-ipfs-image.tar
|
||||
docker-push:
|
||||
executor: dockerizer
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: "18.09.3"
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Load archived Docker image
|
||||
command: docker load -i /tmp/workspace/go-ipfs-image.tar
|
||||
- run:
|
||||
name: Publish Docker Image to Docker Hub
|
||||
command: |
|
||||
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
|
||||
./bin/push-docker-tags.sh $(date -u +%F) "$CIRCLE_SHA1" "$CIRCLE_BRANCH" "$CIRCLE_TAG"
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
# Runs for all branches, but not on tags
|
||||
# see: https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag
|
||||
test:
|
||||
jobs:
|
||||
- gobuild
|
||||
@ -327,3 +364,35 @@ workflows:
|
||||
- ipfs-webui:
|
||||
requires:
|
||||
- build
|
||||
- docker-build
|
||||
- docker-push:
|
||||
# Requires dockerhub credentials, from circleci context.
|
||||
context: dockerhub
|
||||
requires:
|
||||
- docker-build
|
||||
- golint
|
||||
- gotest
|
||||
- sharness
|
||||
- interop
|
||||
- go-ipfs-api
|
||||
- go-ipfs-http-client
|
||||
- ipfs-webui
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- feat/stabilize-dht
|
||||
|
||||
# NOTE: CircleCI only builds tags if you explicitly filter for them. That
|
||||
# also means tag-based jobs can only depend on other tag-based jobs, so we
|
||||
# use a separate workflow because every job needs to be tagged together.
|
||||
# see: https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag
|
||||
docker-on-tag:
|
||||
jobs:
|
||||
- docker-build:
|
||||
filters: *only-version-tags
|
||||
- docker-push:
|
||||
context: dockerhub
|
||||
filters: *only-version-tags
|
||||
requires:
|
||||
- docker-build
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -9,6 +9,8 @@ LICENSE text eol=auto
|
||||
*.png binary
|
||||
*.tar binary
|
||||
*.gz binary
|
||||
*.xz binary
|
||||
*.car binary
|
||||
|
||||
# Binary assets
|
||||
assets/init-doc/* binary
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug-report.md
vendored
3
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -1,8 +1,7 @@
|
||||
---
|
||||
name: 'Bug Report'
|
||||
about: 'Report a bug in go-ipfs.'
|
||||
labels:
|
||||
- bug
|
||||
labels: kind/bug, need/triage
|
||||
---
|
||||
|
||||
#### Version information:
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Getting Help on IPFS
|
||||
url: https://ipfs.io/help
|
||||
about: All information about how and where to get help on IPFS.
|
||||
- name: Go-ipfs configuration reference
|
||||
url: https://github.com/ipfs/go-ipfs/blob/master/docs/config.md
|
||||
about: Documentation on the different configuration settings
|
||||
- name: Go-ipfs experimental features docs
|
||||
url: https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md
|
||||
about: Documentation on Private Networks, Filestore and other experimental features.
|
||||
- name: HTTP API Reference
|
||||
url: https://docs-beta.ipfs.io/reference/http/api/#api-v0-add
|
||||
about: Documentation of all go-ipfs HTTP API endpoints.
|
||||
- name: IPFS Official Forum
|
||||
url: https://discuss.ipfs.io
|
||||
about: Please post general questions, support requests, and discussions here.
|
||||
8
.github/ISSUE_TEMPLATE/doc.md
vendored
8
.github/ISSUE_TEMPLATE/doc.md
vendored
@ -1,13 +1,13 @@
|
||||
---
|
||||
name: 'Documentation Issue'
|
||||
about: 'Report missing/erroneous documentation, propose new documentation, report broken links, etc.'
|
||||
labels:
|
||||
- documentation
|
||||
about: 'Report missing, erroneous docs, broken links or propose new go-ipfs docs'
|
||||
labels: topic/docs-ipfs, need/triage
|
||||
---
|
||||
<!-- Problems with documentation on https://docs.ipfs.io should be reported to https://github.com/ipfs/docs -->
|
||||
|
||||
#### Location
|
||||
|
||||
<!-- In the case of missing/erroneous documentation, where is the error? If possible, a link/url would be great! -->
|
||||
<!-- In the case of missing/erroneous documentation, where is the error? If possible, a link/URL would be great! -->
|
||||
|
||||
#### Description
|
||||
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/enhancement.md
vendored
5
.github/ISSUE_TEMPLATE/enhancement.md
vendored
@ -1,12 +1,11 @@
|
||||
---
|
||||
name: 'Enhancement'
|
||||
about: 'Suggest an improvement to an existing go-ipfs feature.'
|
||||
labels:
|
||||
- enhancement
|
||||
labels: kind/enhancement
|
||||
---
|
||||
|
||||
<!--
|
||||
Note: If you'd like to suggest an idea related to IPFS but not specifically related to the go implementation, please file an issue at https://github.com/ipfs/ipfs instead
|
||||
Note: If you'd like to suggest an idea related to IPFS but not specifically related to the Go implementation, please post in https://discuss.ipfs.io instead.
|
||||
|
||||
When requesting an _enhancement_, please be sure to include your motivation and try to be as specific as possible.
|
||||
-->
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature.md
vendored
5
.github/ISSUE_TEMPLATE/feature.md
vendored
@ -1,12 +1,11 @@
|
||||
---
|
||||
name: 'Feature'
|
||||
about: 'Suggest a new feature in go-ipfs.'
|
||||
labels:
|
||||
- feature
|
||||
labels: kind/feature, need/triage
|
||||
---
|
||||
|
||||
<!--
|
||||
Note: If you'd like to suggest an idea related to IPFS but not specifically related to the go implementation, please file an issue at https://github.com/ipfs/ipfs instead
|
||||
Note: If you'd like to suggest an idea related to IPFS but not specifically related to the Go implementation, please post in https://discuss.ipfs.io instead.
|
||||
|
||||
When requesting a _feature_, please be sure to include:
|
||||
* Your motivation. Why do you need the feature?
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/question.md
vendored
11
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,11 +0,0 @@
|
||||
---
|
||||
name: 'Question/Support'
|
||||
about: 'Ask a question about go-ipfs or request support.'
|
||||
labels:
|
||||
- question
|
||||
- invalid
|
||||
---
|
||||
|
||||
This bug tracker is only for actionable bug reports and feature requests. Please direct any questions to https://discuss.ipfs.io or to our Matrix (#ipfs:matrix.org) or IRC (#ipfs on freenode) channels.
|
||||
|
||||
If you don't get an immediate response, please keep trying.
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,6 +15,7 @@ gx-workspace-update.json
|
||||
|
||||
.ipfs
|
||||
bin/gx
|
||||
bin/protoc-*
|
||||
bin/gx*
|
||||
bin/tmp
|
||||
bin/gocovmerge
|
||||
@ -24,3 +25,5 @@ bin/cover
|
||||
vendor
|
||||
.tarball
|
||||
go-ipfs-source.tar.gz
|
||||
docs/examples/go-ipfs-as-a-library/example-folder/Qm*
|
||||
/test/sharness/t0054-dag-car-import-export-data/*.car
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "assets/dir-index-html"]
|
||||
path = assets/dir-index-html
|
||||
url = https://github.com/ipfs/dir-index-html.git
|
||||
1618
CHANGELOG.md
1618
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
50
Dockerfile
50
Dockerfile
@ -1,5 +1,11 @@
|
||||
FROM golang:1.12-stretch
|
||||
MAINTAINER Lars Gierth <lgierth@ipfs.io>
|
||||
FROM golang:1.13.10-buster
|
||||
LABEL maintainer="Steven Allen <steven@stebalien.com>"
|
||||
|
||||
# Install deps
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libssl-dev \
|
||||
ca-certificates \
|
||||
fuse
|
||||
|
||||
ENV SRC_DIR /go-ipfs
|
||||
|
||||
@ -10,35 +16,38 @@ RUN cd $SRC_DIR \
|
||||
|
||||
COPY . $SRC_DIR
|
||||
|
||||
# Preload an in-tree but disabled-by-default plugin by adding it to the IPFS_PLUGINS variable
|
||||
# e.g. docker build --build-arg IPFS_PLUGINS="foo bar baz"
|
||||
ARG IPFS_PLUGINS
|
||||
|
||||
# Build the thing.
|
||||
# Also: fix getting HEAD commit hash via git rev-parse.
|
||||
RUN cd $SRC_DIR \
|
||||
&& mkdir .git/objects \
|
||||
&& make build
|
||||
&& make build GOTAGS=openssl IPFS_PLUGINS=$IPFS_PLUGINS
|
||||
|
||||
# Get su-exec, a very minimal tool for dropping privileges,
|
||||
# and tini, a very minimal init daemon for containers
|
||||
ENV SUEXEC_VERSION v0.2
|
||||
ENV TINI_VERSION v0.16.1
|
||||
RUN set -x \
|
||||
&& cd /tmp \
|
||||
ENV TINI_VERSION v0.19.0
|
||||
RUN set -eux; \
|
||||
dpkgArch="$(dpkg --print-architecture)"; \
|
||||
case "${dpkgArch##*-}" in \
|
||||
"amd64" | "armhf" | "arm64") tiniArch="tini-$dpkgArch" ;;\
|
||||
*) echo >&2 "unsupported architecture: ${dpkgArch}"; exit 1 ;; \
|
||||
esac; \
|
||||
cd /tmp \
|
||||
&& git clone https://github.com/ncopa/su-exec.git \
|
||||
&& cd su-exec \
|
||||
&& git checkout -q $SUEXEC_VERSION \
|
||||
&& make \
|
||||
&& cd /tmp \
|
||||
&& wget -q -O tini https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini \
|
||||
&& wget -q -O tini https://github.com/krallin/tini/releases/download/$TINI_VERSION/$tiniArch \
|
||||
&& chmod +x tini
|
||||
|
||||
# Get the TLS CA certificates, they're not provided by busybox.
|
||||
RUN apt-get update && apt-get install -y ca-certificates
|
||||
|
||||
# Install FUSE
|
||||
RUN apt-get update && apt-get install -y fuse
|
||||
|
||||
# Now comes the actual target image, which aims to be as small as possible.
|
||||
FROM busybox:1-glibc
|
||||
MAINTAINER Lars Gierth <lgierth@ipfs.io>
|
||||
FROM busybox:1.31.1-glibc
|
||||
LABEL maintainer="Steven Allen <steven@stebalien.com>"
|
||||
|
||||
# Get the ipfs binary, entrypoint script, and TLS CAs from the build container.
|
||||
ENV SRC_DIR /go-ipfs
|
||||
@ -52,8 +61,15 @@ COPY --from=0 /etc/ssl/certs /etc/ssl/certs
|
||||
# Add suid bit on fusermount so it will run properly
|
||||
RUN chmod 4755 /usr/local/bin/fusermount
|
||||
|
||||
# Fix permissions on start_ipfs (ignore the build machine's permissions)
|
||||
RUN chmod 0755 /usr/local/bin/start_ipfs
|
||||
|
||||
# This shared lib (part of glibc) doesn't seem to be included with busybox.
|
||||
COPY --from=0 /lib/x86_64-linux-gnu/libdl-2.24.so /lib/libdl.so.2
|
||||
COPY --from=0 /lib/*-linux-gnu*/libdl.so.2 /lib/
|
||||
|
||||
# Copy over SSL libraries.
|
||||
COPY --from=0 /usr/lib/*-linux-gnu*/libssl.so* /usr/lib/
|
||||
COPY --from=0 /usr/lib/*-linux-gnu*/libcrypto.so* /usr/lib/
|
||||
|
||||
# Swarm TCP; should be exposed to the public
|
||||
EXPOSE 4001
|
||||
@ -76,7 +92,7 @@ RUN mkdir /ipfs /ipns \
|
||||
|
||||
# Expose the fs-repo as a volume.
|
||||
# start_ipfs initializes an fs-repo if none is mounted.
|
||||
# Important this happens after the USER directive so permission are correct.
|
||||
# Important this happens after the USER directive so permissions are correct.
|
||||
VOLUME $IPFS_PATH
|
||||
|
||||
# The default logging level
|
||||
|
||||
8
GNUmakefile
Normal file
8
GNUmakefile
Normal file
@ -0,0 +1,8 @@
|
||||
# General tools
|
||||
|
||||
SHELL=PATH='$(PATH)' /bin/sh
|
||||
|
||||
# enable second expansion
|
||||
.SECONDEXPANSION:
|
||||
|
||||
include Rules.mk
|
||||
@ -3,3 +3,6 @@ Unless otherwise noted, all code contributed prior to 2019-05-06 and not contrib
|
||||
a user listed in [this signoff issue](https://github.com/ipfs/go-ipfs/issues/6302) is
|
||||
licensed under MIT-only. All new contributions (and past contributions since 2019-05-06)
|
||||
are licensed under a dual MIT/Apache-2.0 license.
|
||||
|
||||
MIT: https://www.opensource.org/licenses/mit
|
||||
Apache-2.0: https://www.apache.org/licenses/license-2.0
|
||||
14
Makefile
14
Makefile
@ -1,10 +1,6 @@
|
||||
# General tools
|
||||
all:
|
||||
@gmake $@
|
||||
.PHONY: all
|
||||
|
||||
SHELL=PATH='$(PATH)' /bin/sh
|
||||
|
||||
PROTOC = protoc --gogofaster_out=. --proto_path=.:$(GOPATH)/src:$(dir $@) $<
|
||||
|
||||
# enable second expansion
|
||||
.SECONDEXPANSION:
|
||||
|
||||
include Rules.mk
|
||||
.DEFAULT:
|
||||
@gmake $@
|
||||
|
||||
247
README.md
247
README.md
@ -3,21 +3,25 @@
|
||||

|
||||
|
||||
[](http://ipn.io)
|
||||
[](http://ipfs.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://matrix.to/#/room/#ipfs:matrix.org)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://discord.gg/24fmuwR)
|
||||
[](https://godoc.org/github.com/ipfs/go-ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/ipfs/go-ipfs)
|
||||
[](https://travis-ci.com/ipfs/go-ipfs)
|
||||
[](https://circleci.com/gh/ipfs/go-ipfs)
|
||||
|
||||
## What is IPFS?
|
||||
|
||||
IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas from Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single bittorrent swarm, exchanging git objects. IPFS provides an interface as simple as the HTTP web, but with permanence built in. You can also mount the world at /ipfs.
|
||||
IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas from previous systems such Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single bittorrent swarm, exchanging git objects. IPFS provides an interface as simple as the HTTP web, but with permanence built in. You can also mount the world at /ipfs.
|
||||
|
||||
For more info see: https://github.com/ipfs/ipfs.
|
||||
For more info see: https://docs.ipfs.io/introduction/overview/
|
||||
|
||||
Please put all issues regarding:
|
||||
- IPFS _design_ in the [ipfs repo issues](https://github.com/ipfs/ipfs/issues).
|
||||
- Go IPFS _implementation_ in [this repo](https://github.com/ipfs/go-ipfs/issues).
|
||||
Before opening an issue, consider using one of the following locations to ensure you are opening your thread in the right place:
|
||||
- go-ipfs _implementation_ bugs in [this repo](https://github.com/ipfs/go-ipfs/issues).
|
||||
- Documentation issues in [ipfs/docs issues](https://github.com/ipfs/docs/issues).
|
||||
- IPFS _design_ in [ipfs/specs issues](https://github.com/ipfs/specs/issues).
|
||||
- Exploration of new ideas in [ipfs/notes issues](https://github.com/ipfs/notes/issues).
|
||||
- Ask questions and meet the rest of the community at the [IPFS Forum](https://discuss.ipfs.io).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -30,15 +34,17 @@ Please put all issues regarding:
|
||||
- [Install Go](#install-go)
|
||||
- [Download and Compile IPFS](#download-and-compile-ipfs)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Development Dependencies](#development-dependencies)
|
||||
- [Updating](#updating-go-ipfs)
|
||||
- [Usage](#usage)
|
||||
- [Updating go-ipfs](#updating-go-ipfs)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Some things to try](#some-things-to-try)
|
||||
- [Docker usage](#docker-usage)
|
||||
- [Usage](#usage)
|
||||
- [Running IPFS inside Docker](#running-ipfs-inside-docker)
|
||||
- [Troubleshooting](#troubleshooting-1)
|
||||
- [Packages](#packages)
|
||||
- [Development](#development)
|
||||
- [CLI, HTTP-API, Architecture Diagram](#cli-http-api-architecture-diagram)
|
||||
- [Testing](#testing)
|
||||
- [Development Dependencies](#development-dependencies)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
@ -46,17 +52,22 @@ Please put all issues regarding:
|
||||
|
||||
The IPFS protocol and its implementations are still in heavy development. This means that there may be problems in our protocols, or there may be mistakes in our implementations. And -- though IPFS is not production-ready yet -- many people are already running nodes in their machines. So we take security vulnerabilities very seriously. If you discover a security issue, please bring it to our attention right away!
|
||||
|
||||
If you find a vulnerability that may affect live deployments -- for example, by exposing a remote execution exploit -- please send your report privately to security@ipfs.io. Please DO NOT file a public issue. The GPG key for security@ipfs.io is [4B9665FB 92636D17 7C7A86D3 50AAE8A9 59B13AF3](https://pgp.mit.edu/pks/lookup?op=get&search=0x50AAE8A959B13AF3).
|
||||
If you find a vulnerability that may affect live deployments -- for example, by exposing a remote execution exploit -- please send your report privately to security@ipfs.io. Please DO NOT file a public issue.
|
||||
|
||||
If the issue is a protocol weakness that cannot be immediately exploited or something not yet deployed, just discuss it openly.
|
||||
|
||||
## Install
|
||||
|
||||
The canonical download instructions for IPFS are over at: https://docs.ipfs.io/introduction/install/. It is **highly suggested** you follow those instructions if you are not interested in working on IPFS development.
|
||||
The canonical download instructions for IPFS are over at: https://docs.ipfs.io/guides/guides/install/. It is **highly recommended** you follow those instructions if you are not interested in working on IPFS development.
|
||||
|
||||
### System Requirements
|
||||
|
||||
IPFS can run on most Linux, macOS, and Windows systems. We recommend running it on a machine with at least 2 GB of RAM (it’ll do fine with only one CPU core), but it should run fine with as little as 1 GB of RAM. On systems with less memory, it may not be completely stable.
|
||||
IPFS can run on most Linux, macOS, and Windows systems. We recommend running it on a machine with at least 2 GB of RAM and 2 CPU cores (go-ipfs is highly parallel). On systems with less memory, it may not be completely stable.
|
||||
|
||||
If your system is resource constrained, we recommend:
|
||||
|
||||
1. Installing OpenSSL and rebuilding go-ipfs manually with `make build GOTAGS=openssl`. See the [download and compile](#download-and-compile-ipfs) section for more information on compiling go-ipfs.
|
||||
2. Initializing your daemon with `ipfs init --profile=lowpower`
|
||||
|
||||
### Install prebuilt packages
|
||||
|
||||
@ -73,6 +84,7 @@ You can also download go-ipfs from this project's GitHub releases page if you ar
|
||||
|
||||
- [Arch Linux](#arch-linux)
|
||||
- [Nix](#nix)
|
||||
- [Solus](#solus)
|
||||
- [Snap](#snap)
|
||||
|
||||
#### Arch Linux
|
||||
@ -96,7 +108,7 @@ For Linux and MacOSX you can use the purely functional package manager [Nix](htt
|
||||
$ nix-env -i ipfs
|
||||
```
|
||||
|
||||
You can also install the Package by using it's attribute name, which is also `ipfs`.
|
||||
You can also install the Package by using its attribute name, which is also `ipfs`.
|
||||
|
||||
#### Guix
|
||||
|
||||
@ -106,6 +118,17 @@ GNU's functional package manager, [Guix](https://www.gnu.org/software/guix/), al
|
||||
$ guix package -i go-ipfs
|
||||
```
|
||||
|
||||
#### Solus
|
||||
|
||||
In solus, go-ipfs is available in the main repository as
|
||||
[go-ipfs](https://dev.getsol.us/source/go-ipfs/repository/master/).
|
||||
|
||||
```
|
||||
$ sudo eopkg install go-ipfs
|
||||
```
|
||||
|
||||
You can also install it through the Solus software center.
|
||||
|
||||
#### Snap
|
||||
|
||||
With snap, in any of the [supported Linux distributions](https://snapcraft.io/docs/core/install):
|
||||
@ -114,11 +137,40 @@ With snap, in any of the [supported Linux distributions](https://snapcraft.io/do
|
||||
$ sudo snap install ipfs
|
||||
```
|
||||
|
||||
### From Windows package managers
|
||||
|
||||
- [Chocolatey](#chocolatey)
|
||||
- [Scoop](#scoop)
|
||||
|
||||
#### Chocolatey
|
||||
|
||||
The package [ipfs](https://chocolatey.org/packages/ipfs) currently points to go-ipfs and is being maintained.
|
||||
|
||||
```Powershell
|
||||
PS> choco install ipfs
|
||||
```
|
||||
|
||||
#### Scoop
|
||||
|
||||
Scoop provides `go-ipfs` in its 'extras' bucket.
|
||||
```Powershell
|
||||
PS> scoop bucket add extras
|
||||
PS> scoop install go-ipfs
|
||||
```
|
||||
|
||||
### Build from Source
|
||||
|
||||
go-ipfs's build system requires Go 1.13 and some standard POSIX build tools:
|
||||
|
||||
* GNU make
|
||||
* Git
|
||||
* GCC (or some other go compatible C Compiler) (optional)
|
||||
|
||||
To build without GCC, build with `CGO_ENABLED=0` (e.g., `make build CGO_ENABLED=0`).
|
||||
|
||||
#### Install Go
|
||||
|
||||
The build process for ipfs requires Go 1.11 or higher. If you don't have it: [Download Go 1.11+](https://golang.org/dl/).
|
||||
The build process for ipfs requires Go 1.12 or higher. If you don't have it: [Download Go 1.12+](https://golang.org/dl/).
|
||||
|
||||
You'll need to add Go's bin directories to your `$PATH` environment variable e.g., by adding these lines to your `/etc/profile` (for a system-wide installation) or `$HOME/.profile`:
|
||||
|
||||
@ -138,12 +190,27 @@ $ cd go-ipfs
|
||||
$ make install
|
||||
```
|
||||
|
||||
If you are building on a non-GNU system, use `gmake` in place of `make`.
|
||||
Unsupported platforms (run `(g)make supported` for a list) will also need to set the `nofuse` gotag during build.
|
||||
Alternatively, you can run `make build` to build the go-ipfs binary (storing it in `cmd/ipfs/ipfs`) without installing it.
|
||||
|
||||
**NOTE:** If you get an error along the lines of "fatal error: stdlib.h: No such file or directory", you're missing a C compiler. Either re-run `make` with `CGO_ENABLED=0` or install GCC.
|
||||
|
||||
##### Cross Compiling
|
||||
|
||||
Compiling for a different platform is as simple as running:
|
||||
|
||||
```
|
||||
$ GOTAGS=nofuse (g)make install
|
||||
make build GOOS=myTargetOS GOARCH=myTargetArchitecture
|
||||
```
|
||||
|
||||
##### OpenSSL
|
||||
|
||||
To build go-ipfs with OpenSSL support, append `GOTAGS=openssl` to your `make` invocation. Building with OpenSSL should significantly reduce the background CPU usage on nodes that frequently make or receive new connections.
|
||||
|
||||
Note: OpenSSL requires CGO support and, by default, CGO is disabled when cross compiling. To cross compile with OpenSSL support, you must:
|
||||
|
||||
1. Install a compiler toolchain for the target platform.
|
||||
2. Set the `CGO_ENABLED=1` environment variable.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
- Separate [instructions are available for building on Windows](docs/windows.md).
|
||||
@ -193,7 +260,26 @@ $ ipfs get /ipns/dist.ipfs.io/go-ipfs/$VERSION/go-ipfs_$VERSION_linux-arm.tar.gz
|
||||
$ ipfs get /ipns/dist.ipfs.io/go-ipfs/$VERSION/go-ipfs_$VERSION_windows-amd64.zip # windows 64-bit build
|
||||
```
|
||||
|
||||
## Usage
|
||||
## Getting Started
|
||||
|
||||
See also: https://docs.ipfs.io/introduction/usage/
|
||||
|
||||
To start using IPFS, you must first initialize IPFS's config files on your
|
||||
system, this is done with `ipfs init`. See `ipfs init --help` for information on
|
||||
the optional arguments it takes. After initialization is complete, you can use
|
||||
`ipfs mount`, `ipfs add` and any of the other commands to explore!
|
||||
|
||||
### Some things to try
|
||||
|
||||
Basic proof of 'ipfs working' locally:
|
||||
|
||||
echo "hello world" > hello
|
||||
ipfs add hello
|
||||
# This should output a hash string that looks something like:
|
||||
# QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o
|
||||
ipfs cat <that hash>
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
ipfs - Global p2p merkle-dag filesystem.
|
||||
@ -245,27 +331,7 @@ SUBCOMMANDS
|
||||
export IPFS_PATH=/path/to/ipfsrepo
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
See also: http://ipfs.io/docs/getting-started/
|
||||
|
||||
To start using IPFS, you must first initialize IPFS's config files on your
|
||||
system, this is done with `ipfs init`. See `ipfs init --help` for information on
|
||||
the optional arguments it takes. After initialization is complete, you can use
|
||||
`ipfs mount`, `ipfs add` and any of the other commands to explore!
|
||||
|
||||
### Some things to try
|
||||
|
||||
Basic proof of 'ipfs working' locally:
|
||||
|
||||
echo "hello world" > hello
|
||||
ipfs add hello
|
||||
# This should output a hash string that looks something like:
|
||||
# QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o
|
||||
ipfs cat <that hash>
|
||||
|
||||
|
||||
### Docker usage
|
||||
### Running IPFS inside Docker
|
||||
|
||||
An IPFS docker image is hosted at [hub.docker.com/r/ipfs/go-ipfs](https://hub.docker.com/r/ipfs/go-ipfs/).
|
||||
To make files visible inside the container you need to mount a host directory
|
||||
@ -312,6 +378,15 @@ When starting a container running ipfs for the first time with an empty data dir
|
||||
|
||||
docker run -d --name ipfs_host -e IPFS_PROFILE=server -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
|
||||
|
||||
It is possible to initialize the container with a swarm key file (`/data/ipfs/swarm.key`) using the variables `IPFS_SWARM_KEY` and `IPFS_SWARM_KEY_FILE`. The `IPFS_SWARM_KEY` creates `swarm.key` with the contents of the variable itself, whilst `IPFS_SWARM_KEY_FILE` copies the key from a path stored in the variable. The `IPFS_SWARM_KEY_FILE` **overwrites** the key generated by `IPFS_SWARM_KEY`.
|
||||
|
||||
docker run -d --name ipfs_host -e IPFS_SWARM_KEY=<your swarm key> -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
|
||||
|
||||
The swarm key initialization can also be done using docker secrets **(requires docker swarm or docker-compose)**:
|
||||
|
||||
cat your_swarm.key | docker secret create swarm_key_secret -
|
||||
docker run -d --name ipfs_host --secret swarm_key_secret -e IPFS_SWARM_KEY_FILE=/run/secrets/swarm_key_secret -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If you have previously installed IPFS before and you are running into problems getting a newer version to work, try deleting (or backing up somewhere else) your IPFS config directory (~/.ipfs by default) and rerunning `ipfs init`. This will reinitialize the config file to its defaults and clear out the local datastore of any bad entries.
|
||||
@ -328,45 +403,62 @@ Listing of the main packages used in the IPFS ecosystem. There are also three sp
|
||||
|
||||
| Name | CI/Travis | Coverage | Description |
|
||||
| ---------|---------|---------|--------- |
|
||||
| **Libp2p** |
|
||||
| [`go-libp2p`](//github.com/libp2p/go-libp2p) | [](https://travis-ci.com/libp2p/go-libp2p) | [](https://codecov.io/gh/libp2p/go-libp2p) | p2p networking library |
|
||||
| [`go-libp2p-pubsub`](//github.com/libp2p/go-libp2p-pubsub) | [](https://travis-ci.com/libp2p/go-libp2p-pubsub) | [](https://codecov.io/gh/libp2p/go-libp2p-pubsub) | pubsub built on libp2p |
|
||||
| [`go-libp2p-kad-dht`](//github.com/libp2p/go-libp2p-kad-dht) | [](https://travis-ci.com/libp2p/go-libp2p-kad-dht) | [](https://codecov.io/gh/libp2p/go-libp2p-kad-dht) | dht-backed router |
|
||||
| [`go-libp2p-pubsub-router`](//github.com/libp2p/go-libp2p-pubsub-router) | [](https://travis-ci.com/libp2p/go-libp2p-pubsub-router) | [](https://codecov.io/gh/libp2p/go-libp2p-pubsub-router) | pubsub-backed router |
|
||||
| **Multiformats** |
|
||||
| [`go-cid`](//github.com/ipfs/go-cid) | [](https://travis-ci.com/ipfs/go-cid) | [](https://codecov.io/gh/ipfs/go-cid) | CID implementation |
|
||||
| [`go-multiaddr`](//github.com/multiformats/go-multiaddr) | [](https://travis-ci.com/multiformats/go-multiaddr) | [](https://codecov.io/gh/multiformats/go-multiaddr) | multiaddr implementation |
|
||||
| [`go-multihash`](//github.com/multiformats/go-multihash) | [](https://travis-ci.com/multiformats/go-multihash) | [](https://codecov.io/gh/multiformats/go-multihash) | multihash implementation |
|
||||
| [`go-multibase`](//github.com/multiformats/go-multibase) | [](https://travis-ci.com/multiformats/go-multibase) | [](https://codecov.io/gh/multiformats/go-multibase) | mulitbase implementation |
|
||||
| **Files** |
|
||||
| [`go-unixfs`](//github.com/ipfs/go-unixfs) | [](https://travis-ci.com/ipfs/go-unixfs) | [](https://codecov.io/gh/ipfs/go-unixfs) | the core 'filesystem' logic |
|
||||
| [`go-mfs`](//github.com/ipfs/go-mfs) | [](https://travis-ci.com/ipfs/go-mfs) | [](https://codecov.io/gh/ipfs/go-mfs) | a mutable filesystem editor for unixfs |
|
||||
| [`go-ipfs-posinfo`](//github.com/ipfs/go-ipfs-posinfo) | [](https://travis-ci.com/ipfs/go-ipfs-posinfo) | [](https://codecov.io/gh/ipfs/go-ipfs-posinfo) | helper datatypes for the filestore |
|
||||
| [`go-ipfs-chunker`](//github.com/ipfs/go-ipfs-chunker) | [](https://travis-ci.com/ipfs/go-ipfs-chunker) | [](https://codecov.io/gh/ipfs/go-ipfs-chunker) | file chunkers |
|
||||
| [`go-unixfs`](//github.com/ipfs/go-unixfs) | [](https://travis-ci.com/ipfs/go-unixfs) | [](https://codecov.io/gh/ipfs/go-unixfs) | the core 'filesystem' logic |
|
||||
| [`go-mfs`](//github.com/ipfs/go-mfs) | [](https://travis-ci.com/ipfs/go-mfs) | [](https://codecov.io/gh/ipfs/go-mfs) | a mutable filesystem editor for unixfs |
|
||||
| [`go-ipfs-posinfo`](//github.com/ipfs/go-ipfs-posinfo) | [](https://travis-ci.com/ipfs/go-ipfs-posinfo) | [](https://codecov.io/gh/ipfs/go-ipfs-posinfo) | helper datatypes for the filestore |
|
||||
| [`go-ipfs-chunker`](//github.com/ipfs/go-ipfs-chunker) | [](https://travis-ci.com/ipfs/go-ipfs-chunker) | [](https://codecov.io/gh/ipfs/go-ipfs-chunker) | file chunkers |
|
||||
| **Exchange** |
|
||||
| [`go-ipfs-exchange-interface`](//github.com/ipfs/go-ipfs-exchange-interface) | [](https://travis-ci.com/ipfs/go-ipfs-exchange-interface) | [](https://codecov.io/gh/ipfs/go-ipfs-exchange-interface) | exchange service interface |
|
||||
| [`go-ipfs-exchange-offline`](//github.com/ipfs/go-ipfs-exchange-offline) | [](https://travis-ci.com/ipfs/go-ipfs-exchange-offline) | [](https://codecov.io/gh/ipfs/go-ipfs-exchange-offline) | (dummy) offline implementation of the exchange service |
|
||||
| [`go-bitswap`](//github.com/ipfs/go-bitswap) | [](https://travis-ci.com/ipfs/go-bitswap) | [](https://codecov.io/gh/ipfs/go-bitswap) | bitswap protocol implementation |
|
||||
| [`go-blockservice`](//github.com/ipfs/go-blockservice) | [](https://travis-ci.com/ipfs/go-blockservice) | [](https://codecov.io/gh/ipfs/go-blockservice) | service that plugs a blockstore and an exchange together |
|
||||
| [`go-ipfs-exchange-interface`](//github.com/ipfs/go-ipfs-exchange-interface) | [](https://travis-ci.com/ipfs/go-ipfs-exchange-interface) | [](https://codecov.io/gh/ipfs/go-ipfs-exchange-interface) | exchange service interface |
|
||||
| [`go-ipfs-exchange-offline`](//github.com/ipfs/go-ipfs-exchange-offline) | [](https://travis-ci.com/ipfs/go-ipfs-exchange-offline) | [](https://codecov.io/gh/ipfs/go-ipfs-exchange-offline) | (dummy) offline implementation of the exchange service |
|
||||
| [`go-bitswap`](//github.com/ipfs/go-bitswap) | [](https://travis-ci.com/ipfs/go-bitswap) | [](https://codecov.io/gh/ipfs/go-bitswap) | bitswap protocol implementation |
|
||||
| [`go-blockservice`](//github.com/ipfs/go-blockservice) | [](https://travis-ci.com/ipfs/go-blockservice) | [](https://codecov.io/gh/ipfs/go-blockservice) | service that plugs a blockstore and an exchange together |
|
||||
| **Datastores** |
|
||||
| [`go-datastore`](//github.com/ipfs/go-datastore) | [](https://travis-ci.com/ipfs/go-datastore) | [](https://codecov.io/gh/ipfs/go-datastore) | datastore interfaces, adapters, and basic implementations |
|
||||
| [`go-ipfs-ds-help`](//github.com/ipfs/go-ipfs-ds-help) | [](https://travis-ci.com/ipfs/go-ipfs-ds-help) | [](https://codecov.io/gh/ipfs/go-ipfs-ds-help) | datastore utility functions |
|
||||
| [`go-ds-flatfs`](//github.com/ipfs/go-ds-flatfs) | [](https://travis-ci.com/ipfs/go-ds-flatfs) | [](https://codecov.io/gh/ipfs/go-ds-flatfs) | a filesystem-based datastore |
|
||||
| [`go-ds-measure`](//github.com/ipfs/go-ds-measure) | [](https://travis-ci.com/ipfs/go-ds-measure) | [](https://codecov.io/gh/ipfs/go-ds-measure) | a metric-collecting database adapter |
|
||||
| [`go-ds-leveldb`](//github.com/ipfs/go-ds-leveldb) | [](https://travis-ci.com/ipfs/go-ds-leveldb) | [](https://codecov.io/gh/ipfs/go-ds-leveldb) | a leveldb based datastore |
|
||||
| [`go-ds-badger`](//github.com/ipfs/go-ds-badger) | [](https://travis-ci.com/ipfs/go-ds-badger) | [](https://codecov.io/gh/ipfs/go-ds-badger) | a badgerdb based datastore |
|
||||
| [`go-datastore`](//github.com/ipfs/go-datastore) | [](https://travis-ci.com/ipfs/go-datastore) | [](https://codecov.io/gh/ipfs/go-datastore) | datastore interfaces, adapters, and basic implementations |
|
||||
| [`go-ipfs-ds-help`](//github.com/ipfs/go-ipfs-ds-help) | [](https://travis-ci.com/ipfs/go-ipfs-ds-help) | [](https://codecov.io/gh/ipfs/go-ipfs-ds-help) | datastore utility functions |
|
||||
| [`go-ds-flatfs`](//github.com/ipfs/go-ds-flatfs) | [](https://travis-ci.com/ipfs/go-ds-flatfs) | [](https://codecov.io/gh/ipfs/go-ds-flatfs) | a filesystem-based datastore |
|
||||
| [`go-ds-measure`](//github.com/ipfs/go-ds-measure) | [](https://travis-ci.com/ipfs/go-ds-measure) | [](https://codecov.io/gh/ipfs/go-ds-measure) | a metric-collecting database adapter |
|
||||
| [`go-ds-leveldb`](//github.com/ipfs/go-ds-leveldb) | [](https://travis-ci.com/ipfs/go-ds-leveldb) | [](https://codecov.io/gh/ipfs/go-ds-leveldb) | a leveldb based datastore |
|
||||
| [`go-ds-badger`](//github.com/ipfs/go-ds-badger) | [](https://travis-ci.com/ipfs/go-ds-badger) | [](https://codecov.io/gh/ipfs/go-ds-badger) | a badgerdb based datastore |
|
||||
| **Namesys** |
|
||||
| [`go-ipns`](//github.com/ipfs/go-ipns) | [](https://travis-ci.com/ipfs/go-ipns) | [](https://codecov.io/gh/ipfs/go-ipns) | IPNS datastructures and validation logic |
|
||||
| [`go-ipns`](//github.com/ipfs/go-ipns) | [](https://travis-ci.com/ipfs/go-ipns) | [](https://codecov.io/gh/ipfs/go-ipns) | IPNS datastructures and validation logic |
|
||||
| **Repo** |
|
||||
| [`go-ipfs-config`](//github.com/ipfs/go-ipfs-config) | [](https://travis-ci.com/ipfs/go-ipfs-config) | [](https://codecov.io/gh/ipfs/go-ipfs-config) | go-ipfs config file definitions |
|
||||
| [`go-fs-lock`](//github.com/ipfs/go-fs-lock) | [](https://travis-ci.com/ipfs/go-fs-lock) | [](https://codecov.io/gh/ipfs/go-fs-lock) | lockfile management functions |
|
||||
| [`fs-repo-migrations`](//github.com/ipfs/fs-repo-migrations) | [](https://travis-ci.com/ipfs/fs-repo-migrations) | [](https://codecov.io/gh/ipfs/fs-repo-migrations) | repo migrations |
|
||||
| **Blocks** |
|
||||
| [`go-block-format`](//github.com/ipfs/go-block-format) | [](https://travis-ci.com/ipfs/go-block-format) | [](https://codecov.io/gh/ipfs/go-block-format) | block interfaces and implementations |
|
||||
| [`go-ipfs-blockstore`](//github.com/ipfs/go-ipfs-blockstore) | [](https://travis-ci.com/ipfs/go-ipfs-blockstore) | [](https://codecov.io/gh/ipfs/go-ipfs-blockstore) | blockstore interfaces and implementations |
|
||||
| [`go-ipfs-config`](//github.com/ipfs/go-ipfs-config) | [](https://travis-ci.com/ipfs/go-ipfs-config) | [](https://codecov.io/gh/ipfs/go-ipfs-config) | go-ipfs config file definitions |
|
||||
| [`go-fs-lock`](//github.com/ipfs/go-fs-lock) | [](https://travis-ci.com/ipfs/go-fs-lock) | [](https://codecov.io/gh/ipfs/go-fs-lock) | lockfile management functions |
|
||||
| [`fs-repo-migrations`](//github.com/ipfs/fs-repo-migrations) | [](https://travis-ci.com/ipfs/fs-repo-migrations) | [](https://codecov.io/gh/ipfs/fs-repo-migrations) | repo migrations |
|
||||
| **IPLD** |
|
||||
| [`go-block-format`](//github.com/ipfs/go-block-format) | [](https://travis-ci.com/ipfs/go-block-format) | [](https://codecov.io/gh/ipfs/go-block-format) | block interfaces and implementations |
|
||||
| [`go-ipfs-blockstore`](//github.com/ipfs/go-ipfs-blockstore) | [](https://travis-ci.com/ipfs/go-ipfs-blockstore) | [](https://codecov.io/gh/ipfs/go-ipfs-blockstore) | blockstore interfaces and implementations |
|
||||
| [`go-ipld-format`](//github.com/ipfs/go-ipld-format) | [](https://travis-ci.com/ipfs/go-ipld-format) | [](https://codecov.io/gh/ipfs/go-ipld-format) | IPLD interfaces |
|
||||
| [`go-ipld-cbor`](//github.com/ipfs/go-ipld-cbor) | [](https://travis-ci.com/ipfs/go-ipld-cbor) | [](https://codecov.io/gh/ipfs/go-ipld-cbor) | IPLD-CBOR implementation |
|
||||
| [`go-ipld-git`](//github.com/ipfs/go-ipld-git) | [](https://travis-ci.com/ipfs/go-ipld-git) | [](https://codecov.io/gh/ipfs/go-ipld-git) | IPLD-Git implementation |
|
||||
| [`go-merkledag`](//github.com/ipfs/go-merkledag) | [](https://travis-ci.com/ipfs/go-merkledag) | [](https://codecov.io/gh/ipfs/go-merkledag) | IPLD-Merkledag implementation (and then some) |
|
||||
| **Commands** |
|
||||
| [`go-ipfs-cmds`](//github.com/ipfs/go-ipfs-cmds) | [](https://travis-ci.com/ipfs/go-ipfs-cmds) | [](https://codecov.io/gh/ipfs/go-ipfs-cmds) | CLI & HTTP commands library |
|
||||
| [`go-ipfs-api`](//github.com/ipfs/go-ipfs-api) | [](https://travis-ci.com/ipfs/go-ipfs-api) | [](https://codecov.io/gh/ipfs/go-ipfs-api) | a shell for the IPFS HTTP API |
|
||||
| [`go-ipfs-cmds`](//github.com/ipfs/go-ipfs-cmds) | [](https://travis-ci.com/ipfs/go-ipfs-cmds) | [](https://codecov.io/gh/ipfs/go-ipfs-cmds) | CLI & HTTP commands library |
|
||||
| [`go-ipfs-files`](//github.com/ipfs/go-ipfs-files) | [](https://travis-ci.com/ipfs/go-ipfs-files) | [](https://codecov.io/gh/ipfs/go-ipfs-files) | CLI & HTTP commands library |
|
||||
| [`go-ipfs-api`](//github.com/ipfs/go-ipfs-api) | [](https://travis-ci.com/ipfs/go-ipfs-api) | [](https://codecov.io/gh/ipfs/go-ipfs-api) | an old, stable shell for the IPFS HTTP API |
|
||||
| [`go-ipfs-http-client`](//github.com/ipfs/go-ipfs-http-client) | [](https://travis-ci.com/ipfs/go-ipfs-http-client) | [](https://codecov.io/gh/ipfs/go-ipfs-http-client) | a new, unstable shell for the IPFS HTTP API |
|
||||
| [`interface-go-ipfs-core`](//github.com/ipfs/interface-go-ipfs-core) | [](https://travis-ci.com/ipfs/interface-go-ipfs-core) | [](https://codecov.io/gh/ipfs/interface-go-ipfs-core) | core go-ipfs API interface definitions |
|
||||
| **Metrics & Logging** |
|
||||
| [`go-metrics-interface`](//github.com/ipfs/go-metrics-interface) | [](https://travis-ci.com/ipfs/go-metrics-interface) | [](https://codecov.io/gh/ipfs/go-metrics-interface) | metrics collection interfaces |
|
||||
| [`go-metrics-prometheus`](//github.com/ipfs/go-metrics-prometheus) | [](https://travis-ci.com/ipfs/go-metrics-prometheus) | [](https://codecov.io/gh/ipfs/go-metrics-prometheus) | prometheus-backed metrics collector |
|
||||
| [`go-log`](//github.com/ipfs/go-log) | [](https://travis-ci.com/ipfs/go-log) | [](https://codecov.io/gh/ipfs/go-log) | logging framework |
|
||||
| [`go-metrics-interface`](//github.com/ipfs/go-metrics-interface) | [](https://travis-ci.com/ipfs/go-metrics-interface) | [](https://codecov.io/gh/ipfs/go-metrics-interface) | metrics collection interfaces |
|
||||
| [`go-metrics-prometheus`](//github.com/ipfs/go-metrics-prometheus) | [](https://travis-ci.com/ipfs/go-metrics-prometheus) | [](https://codecov.io/gh/ipfs/go-metrics-prometheus) | prometheus-backed metrics collector |
|
||||
| [`go-log`](//github.com/ipfs/go-log) | [](https://travis-ci.com/ipfs/go-log) | [](https://codecov.io/gh/ipfs/go-log) | logging framework |
|
||||
| **Generics/Utils** |
|
||||
| [`go-ipfs-routing`](//github.com/ipfs/go-ipfs-routing) | [](https://travis-ci.com/ipfs/go-ipfs-routing) | [](https://codecov.io/gh/ipfs/go-ipfs-routing) | routing (content, peer, value) helpers |
|
||||
| [`go-ipfs-util`](//github.com/ipfs/go-ipfs-util) | [](https://travis-ci.com/ipfs/go-ipfs-util) | [](https://codecov.io/gh/ipfs/go-ipfs-util) | the kitchen sink |
|
||||
| [`go-ipfs-addr`](//github.com/ipfs/go-ipfs-addr) | [](https://travis-ci.com/ipfs/go-ipfs-addr) | [](https://codecov.io/gh/ipfs/go-ipfs-addr) | utility functions for parsing IPFS multiaddrs |
|
||||
| [`go-ipfs-routing`](//github.com/ipfs/go-ipfs-routing) | [](https://travis-ci.com/ipfs/go-ipfs-routing) | [](https://codecov.io/gh/ipfs/go-ipfs-routing) | routing (content, peer, value) helpers |
|
||||
| [`go-ipfs-util`](//github.com/ipfs/go-ipfs-util) | [](https://travis-ci.com/ipfs/go-ipfs-util) | [](https://codecov.io/gh/ipfs/go-ipfs-util) | the kitchen sink |
|
||||
| [`go-ipfs-addr`](//github.com/ipfs/go-ipfs-addr) | [](https://travis-ci.com/ipfs/go-ipfs-addr) | [](https://codecov.io/gh/ipfs/go-ipfs-addr) | utility functions for parsing IPFS multiaddrs |
|
||||
|
||||
For brevity, we've omitted go-libp2p and go-ipld packages. These package tables can be found in their respective project's READMEs:
|
||||
For brevity, we've omitted most go-libp2p, go-ipld, and go-multiformats packages. These package tables can be found in their respective project's READMEs:
|
||||
|
||||
* [go-libp2p](https://github.com/libp2p/go-libp2p#packages)
|
||||
* [go-ipld](https://github.com/ipld/go-ipld#packages)
|
||||
@ -384,6 +476,10 @@ Some places to get you started on the codebase:
|
||||
- PubSub: https://github.com/libp2p/go-libp2p-pubsub
|
||||
- [IPFS : The `Add` command demystified](https://github.com/ipfs/go-ipfs/tree/master/docs/add-code-flow.md)
|
||||
|
||||
### Map of go-ipfs Subsystems
|
||||
**WIP**: This is a high-level architecture diagram of the various sub-systems of go-ipfs. To be updated with how they interact. Anyone who has suggestions is welcome to comment [here](https://docs.google.com/drawings/d/1OVpBT2q-NtSJqlPX3buvjYhOnWfdzb85YEsM_njesME/edit) on how we can improve this!
|
||||
<img src="https://docs.google.com/drawings/d/e/2PACX-1vS_n1FvSu6mdmSirkBrIIEib2gqhgtatD9awaP2_WdrGN4zTNeg620XQd9P95WT-IvognSxIIdCM5uE/pub?w=1446&h=1036">
|
||||
|
||||
### CLI, HTTP-API, Architecture Diagram
|
||||
|
||||

|
||||
@ -419,4 +515,7 @@ You can contact us on the freenode #ipfs-dev channel or attend one of our
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
The go-ipfs project is dual-licensed under Apache 2.0 and MIT terms:
|
||||
|
||||
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/ipfs/go-ipfs/blob/master/LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](https://github.com/ipfs/go-ipfs/blob/master/LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
12
Rules.mk
12
Rules.mk
@ -5,6 +5,7 @@ DISTCLEAN :=
|
||||
TEST :=
|
||||
TEST_SHORT :=
|
||||
GOCC ?= go
|
||||
PROTOC ?= protoc
|
||||
|
||||
all: help # all has to be first defined target
|
||||
.PHONY: all
|
||||
@ -47,19 +48,12 @@ ifneq ($(filter coverage% clean distclean test/unit/gotest.junit.xml,$(MAKECMDGO
|
||||
include $(dir)/Rules.mk
|
||||
endif
|
||||
|
||||
dir := pin/internal/pb
|
||||
include $(dir)/Rules.mk
|
||||
|
||||
dir := filestore/pb
|
||||
include $(dir)/Rules.mk
|
||||
|
||||
|
||||
# -------------------- #
|
||||
# universal rules #
|
||||
# -------------------- #
|
||||
|
||||
%.pb.go: %.proto
|
||||
$(PROTOC)
|
||||
%.pb.go: %.proto bin/protoc-gen-gogofaster
|
||||
$(PROTOC) --gogofaster_out=. --proto_path=.:$(GOPATH)/src:$(dir $@) $<
|
||||
|
||||
# -------------------- #
|
||||
# core targets #
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
# Assets loaded in with IPFS
|
||||
|
||||
## Generating docs
|
||||
This directory contains the go-ipfs assets:
|
||||
|
||||
* Getting started documentation (`init-doc`).
|
||||
* Directory listing HTML template (`dir-index-html` git submodule).
|
||||
|
||||
These assets are compiled into `bindata.go` with `go generate`.
|
||||
|
||||
## Re-generating
|
||||
|
||||
Do not edit the .go files directly.
|
||||
|
||||
@ -8,6 +15,36 @@ Instead, edit the source files and use `go generate` from within the
|
||||
assets directory:
|
||||
|
||||
```
|
||||
go get -u github.com/jteeuwen/go-bindata/...
|
||||
go generate
|
||||
go generate .
|
||||
```
|
||||
|
||||
## Updating dir-index-html
|
||||
|
||||
Upstream: https://github.com/ipfs/dir-index-html
|
||||
|
||||
dir-index-html is a git submodule. To update, run the following commands from
|
||||
this directory.
|
||||
|
||||
```bash
|
||||
> git -C dir-index-html pull
|
||||
> git -C dir-index-html checkout vX.Y.Z # target version
|
||||
```
|
||||
|
||||
Then, you'll need to commit the updated submodule _before_ regenerating
|
||||
`bindata.go`. Otherwise, `go generate` will checkout the checked-in version of
|
||||
dir-index-html.
|
||||
|
||||
```bash
|
||||
> git add dir-index-html
|
||||
> git commit -m 'chore: update dir-index-html to vX.Y.Z'
|
||||
```
|
||||
|
||||
Finally, re-generate the directory index HTML template and amend the previous
|
||||
commit.
|
||||
|
||||
```bash
|
||||
> go generate .
|
||||
> git add bindata.go
|
||||
> git commit --amend --no-edit
|
||||
|
||||
```
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
//go:generate go-bindata -pkg=assets -prefix=$GOPATH/src/gx/ipfs/QmT1jwrqzSMjSjLG5oBd9w4P9vXPKQksWuf5ghsE3Q88ZV init-doc $GOPATH/src/gx/ipfs/QmT1jwrqzSMjSjLG5oBd9w4P9vXPKQksWuf5ghsE3Q88ZV/dir-index-html
|
||||
//go:generate git submodule update --init ./dir-index-html
|
||||
//go:generate go run github.com/go-bindata/go-bindata/go-bindata -pkg=assets init-doc dir-index-html/dir-index.html dir-index-html/knownIcons.txt
|
||||
//go:generate gofmt -w bindata.go
|
||||
|
||||
package assets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
@ -15,9 +14,6 @@ import (
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
options "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
"github.com/ipfs/interface-go-ipfs-core/path"
|
||||
|
||||
// this import keeps gx from thinking the dep isn't used
|
||||
_ "github.com/ipfs/dir-index-html"
|
||||
)
|
||||
|
||||
// initDocPaths lists the paths for the docs we want to seed during --init
|
||||
@ -36,16 +32,6 @@ func SeedInitDocs(nd *core.IpfsNode) (cid.Cid, error) {
|
||||
return addAssetList(nd, initDocPaths)
|
||||
}
|
||||
|
||||
var initDirPath = filepath.Join(os.Getenv("GOPATH"), "gx", "ipfs", "QmT1jwrqzSMjSjLG5oBd9w4P9vXPKQksWuf5ghsE3Q88ZV", "dir-index-html")
|
||||
var initDirIndex = []string{
|
||||
filepath.Join(initDirPath, "knownIcons.txt"),
|
||||
filepath.Join(initDirPath, "dir-index.html"),
|
||||
}
|
||||
|
||||
func SeedInitDirIndex(nd *core.IpfsNode) (cid.Cid, error) {
|
||||
return addAssetList(nd, initDirIndex)
|
||||
}
|
||||
|
||||
func addAssetList(nd *core.IpfsNode, l []string) (cid.Cid, error) {
|
||||
api, err := coreapi.NewCoreAPI(nd)
|
||||
if err != nil {
|
||||
|
||||
@ -12,14 +12,6 @@ func TestEmbeddedDocs(t *testing.T) {
|
||||
testNFiles(initDocPaths, 5, t, "documents")
|
||||
}
|
||||
|
||||
func TestDirIndex(t *testing.T) {
|
||||
t.Skip("skipping for now, code being tested is currently unused")
|
||||
// TODO: import assets during init.
|
||||
// this will require figuring out how to set the right paths up for
|
||||
// referencing the code from its gx path
|
||||
testNFiles(initDirIndex, 2, t, "assets")
|
||||
}
|
||||
|
||||
func testNFiles(fs []string, wantCnt int, t *testing.T, ftype string) {
|
||||
if len(fs) < wantCnt {
|
||||
t.Fatalf("expected %d %s. got %d", wantCnt, ftype, len(fs))
|
||||
|
||||
File diff suppressed because one or more lines are too long
9
assets/bindata_dep.go
Normal file
9
assets/bindata_dep.go
Normal file
@ -0,0 +1,9 @@
|
||||
//+build never,!never
|
||||
|
||||
package assets
|
||||
|
||||
import (
|
||||
// Make sure go mod tracks these deps but avoid including them in the
|
||||
// actual build.
|
||||
_ "github.com/go-bindata/go-bindata/v3"
|
||||
)
|
||||
1
assets/dir-index-html
Submodule
1
assets/dir-index-html
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 545a1fd882f34f8cd0ae84ece19d458d62471549
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,8 +2,8 @@ include mk/header.mk
|
||||
|
||||
dist_root_$(d)="/ipfs/QmPrXH9jRVwvd7r5MC5e6nV4uauQGzLk1i2647Ye9Vbbwe"
|
||||
|
||||
TGTS_$(d) := $(d)/gx $(d)/gx-go
|
||||
DISTCLEAN += $(wildcard $(d)/gx-v*) $(wildcard $(d)/gx-go-v*) $(d)/tmp
|
||||
TGTS_$(d) := $(d)/protoc
|
||||
DISTCLEAN += $(d)/protoc $(d)/tmp
|
||||
|
||||
PATH := $(realpath $(d)):$(PATH)
|
||||
|
||||
@ -15,5 +15,8 @@ else
|
||||
ln -s $(notdir $^) $@
|
||||
endif
|
||||
|
||||
bin/protoc-gen-gogofaster:
|
||||
$(call go-build,github.com/gogo/protobuf/protoc-gen-gogofaster)
|
||||
|
||||
CLEAN += $(TGTS_$(d))
|
||||
include mk/footer.mk
|
||||
|
||||
@ -29,7 +29,7 @@ PREFIX=$(expr "$0" : "\(.*\/\)") || PREFIX='./'
|
||||
# Include the 'check_at_least_version' function
|
||||
. ${PREFIX}check_version
|
||||
|
||||
# Check that the go binary exist and is in the path
|
||||
# Check that the go binary exists and is in the path
|
||||
|
||||
GOCC=${GOCC="go"}
|
||||
|
||||
|
||||
46
bin/collect-profiles.sh
Normal file → Executable file
46
bin/collect-profiles.sh
Normal file → Executable file
@ -1,39 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
|
||||
# collect-profiles.sh
|
||||
#
|
||||
# Collects go profile information from a running `ipfs` daemon.
|
||||
# Creates an archive including the profiles, profile graph svgs,
|
||||
# ...and where available, a copy of the `ipfs` binary on the PATH.
|
||||
#
|
||||
# Please run this script and attach the profile archive it creates
|
||||
# when reporting bugs at https://github.com/ipfs/go-ipfs/issues
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
HTTP_API="${1:-localhost:5001}"
|
||||
SOURCE_URL="${1:-http://127.0.0.1:5001}"
|
||||
tmpdir=$(mktemp -d)
|
||||
export PPROF_TMPDIR="$tmpdir"
|
||||
pushd "$tmpdir"
|
||||
pushd "$tmpdir" > /dev/null
|
||||
|
||||
if command -v ipfs > /dev/null 2>&1; then
|
||||
cp "$(command -v ipfs)" ipfs
|
||||
fi
|
||||
|
||||
echo Collecting goroutine stacks
|
||||
curl -o goroutines.stacks "http://$HTTP_API"'/debug/pprof/goroutine?debug=2'
|
||||
curl -s -o goroutines.stacks "$SOURCE_URL"'/debug/pprof/goroutine?debug=2'
|
||||
|
||||
echo Collecting goroutine profile
|
||||
go tool pprof -symbolize=remote -svg -output goroutine.svg "http://$HTTP_API/debug/pprof/goroutine"
|
||||
go tool pprof -symbolize=remote -svg -output goroutine.svg "$SOURCE_URL/debug/pprof/goroutine"
|
||||
|
||||
echo Collecting heap profile
|
||||
go tool pprof -symbolize=remote -svg -output heap.svg "http://$HTTP_API/debug/pprof/heap"
|
||||
go tool pprof -symbolize=remote -svg -output heap.svg "$SOURCE_URL/debug/pprof/heap"
|
||||
|
||||
echo "Collecting cpu profile (~30s)"
|
||||
go tool pprof -symbolize=remote -svg -output cpu.svg "http://$HTTP_API/debug/pprof/profile"
|
||||
go tool pprof -symbolize=remote -svg -output cpu.svg "$SOURCE_URL/debug/pprof/profile"
|
||||
|
||||
echo "Enabling mutex profiling"
|
||||
curl -X POST -v "http://$HTTP_API"'/debug/pprof-mutex/?fraction=4'
|
||||
curl -X POST "$SOURCE_URL"'/debug/pprof-mutex/?fraction=4'
|
||||
|
||||
echo "Waiting for mutex data to be updated (30s)"
|
||||
sleep 30
|
||||
curl -o mutex.txt "http://$HTTP_API"'/debug/pprof/mutex?debug=2'
|
||||
go tool pprof -symbolize=remote -svg -output mutex.svg "http://$HTTP_API/debug/pprof/mutex"
|
||||
curl -s -o mutex.txt "$SOURCE_URL"'/debug/pprof/mutex?debug=2'
|
||||
go tool pprof -symbolize=remote -svg -output mutex.svg "$SOURCE_URL/debug/pprof/mutex"
|
||||
|
||||
echo "Disabling mutex profiling"
|
||||
curl -X POST -v "http://$HTTP_API"'/debug/pprof-mutex/?fraction=0'
|
||||
curl -X POST "$SOURCE_URL"'/debug/pprof-mutex/?fraction=0'
|
||||
|
||||
popd
|
||||
tar cvzf "./ipfs-profile-$(uname -n)-$(date -Iseconds).tar.gz" -C "$tmpdir" .
|
||||
OUTPUT_NAME=ipfs-profile-$(uname -n)-$(date +'%Y-%m-%dT%H:%M:%S%z').tar.gz
|
||||
echo "Creating $OUTPUT_NAME"
|
||||
popd > /dev/null
|
||||
tar czf "./$OUTPUT_NAME" -C "$tmpdir" .
|
||||
rm -rf "$tmpdir"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -24,6 +24,33 @@ else
|
||||
ipfs init $INIT_ARGS
|
||||
ipfs config Addresses.API /ip4/0.0.0.0/tcp/5001
|
||||
ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/8080
|
||||
|
||||
# Set up the swarm key, if provided
|
||||
|
||||
SWARM_KEY_FILE="$repo/swarm.key"
|
||||
SWARM_KEY_PERM=0400
|
||||
|
||||
# Create a swarm key from a given environment variable
|
||||
if [ ! -z "$IPFS_SWARM_KEY" ] ; then
|
||||
echo "Copying swarm key from variable..."
|
||||
echo -e "$IPFS_SWARM_KEY" >"$SWARM_KEY_FILE" || exit 1
|
||||
chmod $SWARM_KEY_PERM "$SWARM_KEY_FILE"
|
||||
fi
|
||||
|
||||
# Unset the swarm key variable
|
||||
unset IPFS_SWARM_KEY
|
||||
|
||||
# Check during initialization if a swarm key was provided and
|
||||
# copy it to the ipfs directory with the right permissions
|
||||
# WARNING: This will replace the swarm key if it exists
|
||||
if [ ! -z "$IPFS_SWARM_KEY_FILE" ] ; then
|
||||
echo "Copying swarm key from file..."
|
||||
install -m $SWARM_KEY_PERM "$IPFS_SWARM_KEY_FILE" "$SWARM_KEY_FILE" || exit 1
|
||||
fi
|
||||
|
||||
# Unset the swarm key file variable
|
||||
unset IPFS_SWARM_KEY_FILE
|
||||
|
||||
fi
|
||||
|
||||
exec ipfs "$@"
|
||||
|
||||
@ -63,7 +63,7 @@ dep_changes() {
|
||||
|
||||
# resolve_commits resolves a git ref for each version.
|
||||
resolve_commits() {
|
||||
jq '. + {Ref: (.Version|capture("^((?<ref1>.*)\\+incompatible|v.*-[0-9]{14}-(?<ref2>[a-f0-9]{12})|(?<ref3>v.*))$") | .ref1 // .ref2 // .ref3)}'
|
||||
jq '. + {Ref: (.Version|capture("^((?<ref1>.*)\\+incompatible|v.*-(0\\.)?[0-9]{14}-(?<ref2>[a-f0-9]{12})|(?<ref3>v.*))$") | .ref1 // .ref2 // .ref3)}'
|
||||
}
|
||||
|
||||
# Generate a release log for a range of commits in a single repo.
|
||||
@ -126,6 +126,7 @@ recursive_release_log() {
|
||||
local repo_root="$(git rev-parse --show-toplevel)"
|
||||
local package="$(go list)"
|
||||
(
|
||||
local result=0
|
||||
local workspace="$(mktemp -d)"
|
||||
trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT
|
||||
cd "$workspace"
|
||||
@ -153,9 +154,13 @@ recursive_release_log() {
|
||||
# Compute changelogs
|
||||
jq -r '"\(.Path) \(.New.Version) \(.New.Ref) \(.Old.Version) \(.Old.Ref // "")"' |
|
||||
while read repo new new_ref old old_ref; do
|
||||
ensure "$repo" "$new_ref"
|
||||
statlog "$repo" "$old_ref" "$new_ref" >> statlog.json
|
||||
local changelog="$(release_log "$repo" "$old_ref" "$new_ref")"
|
||||
if ! ensure "$repo" "$new_ref"; then
|
||||
result=1
|
||||
local changelog="failed to fetch repo"
|
||||
else
|
||||
statlog "$repo" "$old_ref" "$new_ref" >> statlog.json
|
||||
local changelog="$(release_log "$repo" "$old_ref" "$new_ref")"
|
||||
fi
|
||||
if [[ -n "$changelog" ]]; then
|
||||
printf -- "- %s (%s -> %s):\n" "$repo" "$old" "$new"
|
||||
echo "$changelog" | indent
|
||||
@ -171,6 +176,7 @@ recursive_release_log() {
|
||||
statsummary <statlog.json |
|
||||
jq -s 'sort_by(.Lines) | reverse | .[]' |
|
||||
jq -r '"| \(.Author) | \(.Commits) | +\(.Insertions)/-\(.Deletions) | \(.Files) |"'
|
||||
return "$status"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
77
bin/push-docker-tags.sh
Executable file
77
bin/push-docker-tags.sh
Executable file
@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# push-docker-tags.sh
|
||||
#
|
||||
# Run from ci to tag images based on the current branch or tag name.
|
||||
# A bit like dockerhub autobuild config, but somewhere we can version control it.
|
||||
#
|
||||
# The `docker-build` job in .circleci/config.yml builds the current commit
|
||||
# in docker and tags it as ipfs/go-ipfs:wip
|
||||
#
|
||||
# Then the `docker-publish` job runs this script to decide what tag, if any,
|
||||
# to publish to dockerhub.
|
||||
#
|
||||
# Usage:
|
||||
# ./push-docker-tags.sh <build number> <git commit sha1> <git branch name> [git tag name] [dry run]
|
||||
#
|
||||
# Example:
|
||||
# # dry run. pass a 5th arg to have it print what it would do rather than do it.
|
||||
# ./push-docker-tags.sh $(date -u +%F) testingsha master "" dryrun
|
||||
#
|
||||
# # push tag for the master branch
|
||||
# ./push-docker-tags.sh $(date -u +%F) testingsha master
|
||||
#
|
||||
# # push tag for a release tag
|
||||
# ./push-docker-tags.sh $(date -u +%F) testingsha release v0.5.0
|
||||
#
|
||||
# # Serving suggestion in circle ci - https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
|
||||
# ./push-docker-tags.sh $(date -u +%F) "$CIRCLE_SHA1" "$CIRCLE_BRANCH" "$CIRCLE_TAG"
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 3 ]] ; then
|
||||
echo 'At least 3 args required. Pass 5 args for a dry run.'
|
||||
echo 'Usage:'
|
||||
echo './push-docker-tags.sh <build number> <git commit sha1> <git branch name> [git tag name] [dry run]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_NUM=$1
|
||||
GIT_SHA1=$2
|
||||
GIT_SHA1_SHORT=$(echo "$GIT_SHA1" | cut -c 1-7)
|
||||
GIT_BRANCH=$3
|
||||
GIT_TAG=${4:-""}
|
||||
DRY_RUN=${5:-false}
|
||||
|
||||
WIP_IMAGE_TAG=${WIP_IMAGE_TAG:-wip}
|
||||
IMAGE_NAME=${IMAGE_NAME:-ipfs/go-ipfs}
|
||||
|
||||
pushTag () {
|
||||
local IMAGE_TAG=$1
|
||||
if [ "$DRY_RUN" != false ]; then
|
||||
echo "DRY RUN! I would have tagged and pushed the following..."
|
||||
echo docker tag "$IMAGE_NAME:$WIP_IMAGE_TAG" "$IMAGE_NAME:$IMAGE_TAG"
|
||||
echo docker push "$IMAGE_NAME:$IMAGE_TAG"
|
||||
else
|
||||
echo "Tagging $IMAGE_NAME:$IMAGE_TAG and pushing to dockerhub"
|
||||
docker tag "$IMAGE_NAME:$WIP_IMAGE_TAG" "$IMAGE_NAME:$IMAGE_TAG"
|
||||
docker push "$IMAGE_NAME:$IMAGE_TAG"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $GIT_TAG =~ ^v[0-9]+ ]]; then
|
||||
pushTag "$GIT_TAG"
|
||||
pushTag "latest"
|
||||
|
||||
elif [ "$GIT_BRANCH" = "feat/stabilize-dht" ]; then
|
||||
pushTag "bifrost-${BUILD_NUM}-${GIT_SHA1_SHORT}"
|
||||
pushTag "bifrost-latest"
|
||||
|
||||
elif [ "$GIT_BRANCH" = "master" ]; then
|
||||
pushTag "master-${BUILD_NUM}-${GIT_SHA1_SHORT}"
|
||||
pushTag "master-latest"
|
||||
|
||||
else
|
||||
echo "Nothing to do. No docker tag defined for branch: $GIT_BRANCH, tag: $GIT_TAG"
|
||||
|
||||
fi
|
||||
@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
T="$(mktemp)"
|
||||
find . -name '*.go' | xargs gofmt -l > "$T"
|
||||
find . \
|
||||
-path ./test/sharness -prune \
|
||||
-o -path ./plugin/loader/preload.go -prune \
|
||||
-o -name '*.go' -print0 | xargs -0 gofmt -l > "$T"
|
||||
|
||||
if [ -n "$(cat $T)" ]; then
|
||||
echo "Following Go code is not formatted."
|
||||
|
||||
@ -2,21 +2,20 @@
|
||||
package blockstoreutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
bs "github.com/ipfs/go-ipfs-blockstore"
|
||||
"github.com/ipfs/go-ipfs-pinner"
|
||||
)
|
||||
|
||||
// RemovedBlock is used to respresent the result of removing a block.
|
||||
// RemovedBlock is used to represent the result of removing a block.
|
||||
// If a block was removed successfully than the Error string will be
|
||||
// empty. If a block could not be removed than Error will contain the
|
||||
// reason the block could not be removed. If the removal was aborted
|
||||
// due to a fatal error Hash will be be empty, Error will contain the
|
||||
// due to a fatal error Hash will be empty, Error will contain the
|
||||
// reason, and no more results will be sent.
|
||||
type RemovedBlock struct {
|
||||
Hash string `json:",omitempty"`
|
||||
@ -34,7 +33,7 @@ type RmBlocksOpts struct {
|
||||
// It returns a channel where objects of type RemovedBlock are placed, when
|
||||
// not using the Quiet option. Block removal is asynchronous and will
|
||||
// skip any pinned blocks.
|
||||
func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) {
|
||||
func RmBlocks(ctx context.Context, blocks bs.GCBlockstore, pins pin.Pinner, cids []cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) {
|
||||
// make the channel large enough to hold any result to avoid
|
||||
// blocking while holding the GCLock
|
||||
out := make(chan interface{}, len(cids))
|
||||
@ -44,13 +43,23 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []cid.Cid, opts RmBl
|
||||
unlocker := blocks.GCLock()
|
||||
defer unlocker.Unlock()
|
||||
|
||||
stillOkay := FilterPinned(pins, out, cids)
|
||||
stillOkay := FilterPinned(ctx, pins, out, cids)
|
||||
|
||||
for _, c := range stillOkay {
|
||||
err := blocks.DeleteBlock(c)
|
||||
if err != nil && opts.Force && (err == bs.ErrNotFound || err == ds.ErrNotFound) {
|
||||
// ignore non-existent blocks
|
||||
} else if err != nil {
|
||||
// Kept for backwards compatibility. We may want to
|
||||
// remove this sometime in the future.
|
||||
has, err := blocks.Has(c)
|
||||
if err != nil {
|
||||
out <- &RemovedBlock{Hash: c.String(), Error: err.Error()}
|
||||
continue
|
||||
}
|
||||
if !has && !opts.Force {
|
||||
out <- &RemovedBlock{Hash: c.String(), Error: bs.ErrNotFound.Error()}
|
||||
continue
|
||||
}
|
||||
|
||||
err = blocks.DeleteBlock(c)
|
||||
if err != nil {
|
||||
out <- &RemovedBlock{Hash: c.String(), Error: err.Error()}
|
||||
} else if !opts.Quiet {
|
||||
out <- &RemovedBlock{Hash: c.String()}
|
||||
@ -65,9 +74,9 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []cid.Cid, opts RmBl
|
||||
// out channel, with an error which indicates that the Cid is pinned.
|
||||
// This function is used in RmBlocks to filter out any blocks which are not
|
||||
// to be removed (because they are pinned).
|
||||
func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []cid.Cid) []cid.Cid {
|
||||
func FilterPinned(ctx context.Context, pins pin.Pinner, out chan<- interface{}, cids []cid.Cid) []cid.Cid {
|
||||
stillOkay := make([]cid.Cid, 0, len(cids))
|
||||
res, err := pins.CheckIfPinned(cids...)
|
||||
res, err := pins.CheckIfPinned(ctx, cids...)
|
||||
if err != nil {
|
||||
out <- &RemovedBlock{Error: fmt.Sprintf("pin check failed: %s", err)}
|
||||
return nil
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
FROM golang:1.11
|
||||
MAINTAINER Jakub Sztandera <kubuxu@ipfs.io>
|
||||
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
netcat-openbsd bash curl \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GOBIN $GOPATH/bin
|
||||
ENV SRC_PATH /go/src/github.com/ipfs/go-ipfs
|
||||
|
||||
RUN curl -s https://codecov.io/bash > /usr/bin/codecov && chmod +x /usr/bin/codecov \
|
||||
&& go get -u github.com/Kubuxu/gocovmerge && go get -u golang.org/x/tools/cmd/cover
|
||||
ENV IPFS_SKIP_COVER_BINS 1
|
||||
|
||||
|
||||
RUN useradd user
|
||||
RUN chown -R user $GOPATH
|
||||
|
||||
WORKDIR $SRC_PATH
|
||||
|
||||
COPY ./bin $SRC_PATH/bin/
|
||||
COPY ./mk $SRC_PATH/mk/
|
||||
RUN chown -R user $GOPATH
|
||||
|
||||
USER user
|
||||
# install gx and gx-go
|
||||
RUN make -j 4 -f bin/Rules.mk d=bin bin/gx bin/gx-go && cp bin/gx bin/gx-go $GOBIN
|
||||
USER root
|
||||
ENV IPFS_GX_USE_GLOBAL 1
|
||||
|
||||
COPY package.json $SRC_PATH/
|
||||
ENV PATH $SRC_PATH/bin:$PATH
|
||||
|
||||
USER user
|
||||
RUN make -f mk/gx.mk gx-deps
|
||||
USER root
|
||||
|
||||
COPY . $SRC_PATH
|
||||
RUN chown -R user:user $GOPATH
|
||||
USER user
|
||||
# mkdir .git/objects is required for git to detect repo
|
||||
RUN mkdir .git/objects && make cmd/ipfs/ipfs #populate go cache
|
||||
|
||||
CMD ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
223
ci/Jenkinsfile
vendored
223
ci/Jenkinsfile
vendored
@ -1,223 +0,0 @@
|
||||
import groovy.transform.Field
|
||||
|
||||
/* SETTINGS */
|
||||
|
||||
// in minutes
|
||||
def sharness_timeout = 15
|
||||
def gotest_timeout = 10
|
||||
def build_timeout = 20
|
||||
def check_timeout = 4
|
||||
|
||||
def test = 'go test -v ./...'
|
||||
|
||||
def fast_build_platforms = [
|
||||
['linux', 'amd64'],
|
||||
]
|
||||
|
||||
def build_platforms = [
|
||||
['windows', '386'],
|
||||
['windows', 'amd64'],
|
||||
|
||||
['linux', 'arm'],
|
||||
['linux', 'arm64'],
|
||||
['linux', '386'],
|
||||
|
||||
['darwin', '386'],
|
||||
['darwin', 'amd64'],
|
||||
|
||||
['freebsd', 'amd64']
|
||||
]
|
||||
|
||||
/* PIPELINE UTILS */
|
||||
|
||||
def setupStep(nodeLabel, f) {
|
||||
node(label: nodeLabel) {
|
||||
def ps = nodeLabel != 'windows' ? '/' : '\\'
|
||||
def psep = nodeLabel != 'windows' ? ':' : ';'
|
||||
|
||||
def root = tool name: '1.11', type: 'go'
|
||||
def jobNameArr = "${JOB_NAME}"
|
||||
def jobName = jobNameArr.split("/")[0..1].join(nodeLabel != 'windows' ? '/' : '\\\\').toLowerCase()
|
||||
def subName = jobNameArr.split("/")[2].toLowerCase()
|
||||
def originalWs = "${WORKSPACE}"
|
||||
|
||||
ws("${originalWs}${ps}src${ps}github.com${ps}${jobName}") {
|
||||
def goEnv = ["GOROOT=${root}", "GOPATH=${originalWs}", "SUBNAME=${subName}", "PATH=$PATH${psep}${root}${ps}bin${psep}${originalWs}${ps}bin"]
|
||||
withEnv(goEnv) {
|
||||
checkout scm
|
||||
|
||||
def run = nodeLabel != 'windows' ? this.&sh : this.&bat
|
||||
|
||||
try {
|
||||
f(run)
|
||||
} finally {
|
||||
cleanWs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def gobuild_step = { list ->
|
||||
setupStep('linux') { run ->
|
||||
timeout(time: build_timeout, unit: 'MINUTES') {
|
||||
run "make gx-deps"
|
||||
|
||||
list.each { platform ->
|
||||
timeout(time: check_timeout, unit: 'MINUTES') {
|
||||
withEnv(["GOOS=${platform[0]}", "GOARCH=${platform[1]}"]) {
|
||||
run "go build -i -ldflags=\"-X github.com/ipfs/go-ipfs/repo/config.CurrentCommit=${env.SUBNAME}-${env.BUILD_NUMBER}\" -o cmd/ipfs/ipfs github.com/ipfs/go-ipfs/cmd/ipfs"
|
||||
run "cp cmd/ipfs/ipfs cmd/ipfs/dist; cd cmd/ipfs/dist; tar -czvf ../go-ipfs_${env.GOOS}-${env.GOARCH}-${env.SUBNAME}-${env.BUILD_NUMBER}.tar.gz ."
|
||||
archiveArtifacts artifacts: "cmd/ipfs/go-ipfs_${env.GOOS}-${env.GOARCH}-${env.SUBNAME}-${env.BUILD_NUMBER}.tar.gz", fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def sharness_step = { run, osname, makeargs, ignore ->
|
||||
timeout(time: sharness_timeout, unit: 'MINUTES') {
|
||||
run "make gx-deps"
|
||||
|
||||
try {
|
||||
run "make -j12 ${makeargs} test/sharness/test-results/sharness.xml CONTINUE_ON_S_FAILURE=1 TEST_NO_FUSE=1 TEST_NO_DOCKER=1"
|
||||
|
||||
try {
|
||||
// archive trash directories if any
|
||||
run "tar -czf sharnessTrashDirs-${osname}.tar.gz test/sharness/trash\\ *"
|
||||
archiveArtifacts artifacts: "sharnessTrashDirs-${osname}.tar.gz", fingerprint: true
|
||||
} catch (_) {}
|
||||
} catch (err) {
|
||||
throw err
|
||||
} finally {
|
||||
if (!ignore) {
|
||||
junit allowEmptyResults: true, testResults: 'test/sharness/test-results/sharness.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def gotest_step = { run ->
|
||||
timeout(time: gotest_timeout, unit: 'MINUTES') {
|
||||
run "make gx-deps"
|
||||
|
||||
try {
|
||||
run test + ' -tags="nofuse" 2>&1 | tee output'
|
||||
run 'cat output | go-junit-report > junit-report-linux.xml'
|
||||
} catch (err) {
|
||||
throw err
|
||||
} finally {
|
||||
junit allowEmptyResults: true, testResults: 'junit-report-*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PIPELINE */
|
||||
|
||||
ansiColor('xterm') { withEnv(['TERM=xterm-color']) {
|
||||
stage('Checks') {
|
||||
parallel(
|
||||
'go fmt': {
|
||||
setupStep('linux') { run ->
|
||||
timeout(time: check_timeout, unit: 'MINUTES') {
|
||||
run 'make test_go_fmt'
|
||||
}
|
||||
}
|
||||
},
|
||||
'go vet': {
|
||||
setupStep('linux') { run ->
|
||||
timeout(time: check_timeout, unit: 'MINUTES') {
|
||||
run 'make gx-deps'
|
||||
run 'go vet ./...'
|
||||
}
|
||||
}
|
||||
},
|
||||
'go build': {
|
||||
gobuild_step(fast_build_platforms)
|
||||
},
|
||||
'gx deps dupes': {
|
||||
setupStep('linux') { run ->
|
||||
timeout(time: check_timeout, unit: 'MINUTES') {
|
||||
run 'make gx-deps'
|
||||
run 'test -z "$(./bin/gx deps dupes)"'
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
stage('Tests') {
|
||||
parallel(
|
||||
'go build (other platforms)': {
|
||||
gobuild_step(build_platforms)
|
||||
},
|
||||
windows: {
|
||||
setupStep('windows') { run ->
|
||||
timeout(time: gotest_timeout, unit: 'MINUTES') {
|
||||
run 'go get -v github.com/jstemmer/go-junit-report github.com/whyrusleeping/gx github.com/whyrusleeping/gx-go'
|
||||
run "gx install --global"
|
||||
|
||||
try {
|
||||
run test + ' -tags="nofuse" > output & type output'
|
||||
run 'type output | go-junit-report > junit-report-windows.xml'
|
||||
} catch (err) {
|
||||
throw err
|
||||
} finally {
|
||||
/* IGNORE TEST FAILS */
|
||||
/* junit allowEmptyResults: true, testResults: 'junit-report-*.xml' */
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
linux: {
|
||||
setupStep('linux') { run ->
|
||||
run 'go get -v github.com/jstemmer/go-junit-report'
|
||||
gotest_step(run)
|
||||
}
|
||||
},
|
||||
linuxSharness: {
|
||||
setupStep('linux') { run ->
|
||||
sharness_step(run, 'linux', '-Otarget', false)
|
||||
}
|
||||
},
|
||||
linux32: {
|
||||
setupStep('linux') { run ->
|
||||
run 'go get -v github.com/jstemmer/go-junit-report'
|
||||
withEnv(["GOARCH=386"]) {
|
||||
gotest_step(run)
|
||||
}
|
||||
}
|
||||
},
|
||||
linux32Sharness: {
|
||||
setupStep('linux') { run ->
|
||||
withEnv(["GOARCH=386", "TEST_NO_PLUGIN=1"]) {
|
||||
sharness_step(run, 'linux', '-Otarget', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
//macOS: {
|
||||
// setupStep('macos') { run ->
|
||||
// sharness_step(run, 'macos', '', true)
|
||||
// }
|
||||
//},
|
||||
//macSharness: {
|
||||
// setupStep('macos') { run ->
|
||||
// timeout(time: sharness_timeout, unit: 'MINUTES') {
|
||||
// run 'go get -v github.com/jstemmer/go-junit-report'
|
||||
// run "make gx-deps"
|
||||
|
||||
// try {
|
||||
// run "make -j12 test/sharness/test-results/sharness.xml CONTINUE_ON_S_FAILURE=1 TEST_NO_FUSE=1"
|
||||
// } catch (err) {
|
||||
// throw err
|
||||
// } finally {
|
||||
// /* IGNORE TEST FAILS */
|
||||
// /* junit allowEmptyResults: true, testResults: 'test/sharness/test-results/sharness.xml' */
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//},
|
||||
)
|
||||
}
|
||||
}}
|
||||
@ -12,32 +12,37 @@ import (
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
|
||||
version "github.com/ipfs/go-ipfs"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
cserial "github.com/ipfs/go-ipfs-config/serialize"
|
||||
utilmain "github.com/ipfs/go-ipfs/cmd/ipfs/util"
|
||||
oldcmds "github.com/ipfs/go-ipfs/commands"
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
commands "github.com/ipfs/go-ipfs/core/commands"
|
||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
||||
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
|
||||
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
||||
libp2p "github.com/ipfs/go-ipfs/core/node/libp2p"
|
||||
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
|
||||
sockets "github.com/libp2p/go-socket-activation"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
mprome "github.com/ipfs/go-metrics-prometheus"
|
||||
goprocess "github.com/jbenet/goprocess"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/multiformats/go-multiaddr-net"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
prometheus "github.com/prometheus/client_golang/prometheus"
|
||||
promauto "github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
const (
|
||||
adjustFDLimitKwd = "manage-fdlimit"
|
||||
enableGCKwd = "enable-gc"
|
||||
initOptionKwd = "init"
|
||||
initConfigOptionKwd = "init-config"
|
||||
initProfileOptionKwd = "init-profile"
|
||||
ipfsMountKwd = "mount-ipfs"
|
||||
ipnsMountKwd = "mount-ipns"
|
||||
@ -48,6 +53,7 @@ const (
|
||||
routingOptionSupernodeKwd = "supernode"
|
||||
routingOptionDHTClientKwd = "dhtclient"
|
||||
routingOptionDHTKwd = "dht"
|
||||
routingOptionDHTServerKwd = "dhtserver"
|
||||
routingOptionNoneKwd = "none"
|
||||
routingOptionDefaultKwd = "default"
|
||||
unencryptTransportKwd = "disable-transport-encryption"
|
||||
@ -116,7 +122,7 @@ You can setup CORS headers the same way:
|
||||
|
||||
Shutdown
|
||||
|
||||
To shutdown the daemon, send a SIGINT signal to it (e.g. by pressing 'Ctrl-C')
|
||||
To shut down the daemon, send a SIGINT signal to it (e.g. by pressing 'Ctrl-C')
|
||||
or send a SIGTERM signal to it (e.g. with 'kill'). It may take a while for the
|
||||
daemon to shutdown gracefully, but it can be killed forcibly by sending a
|
||||
second signal.
|
||||
@ -154,6 +160,7 @@ Headers.
|
||||
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(initOptionKwd, "Initialize ipfs with default settings if not already initialized"),
|
||||
cmds.StringOption(initConfigOptionKwd, "Path to existing configuration file to be loaded during --init"),
|
||||
cmds.StringOption(initProfileOptionKwd, "Configuration profiles to apply for --init. See ipfs init --help for more"),
|
||||
cmds.StringOption(routingOptionKwd, "Overrides the routing option").WithDefault(routingOptionDefaultKwd),
|
||||
cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
|
||||
@ -222,24 +229,27 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
// check transport encryption flag.
|
||||
unencrypted, _ := req.Options[unencryptTransportKwd].(bool)
|
||||
if unencrypted {
|
||||
log.Warningf(`Running with --%s: All connections are UNENCRYPTED.
|
||||
log.Warnf(`Running with --%s: All connections are UNENCRYPTED.
|
||||
You will not be able to connect to regular encrypted networks.`, unencryptTransportKwd)
|
||||
}
|
||||
|
||||
// first, whether user has provided the initialization flag. we may be
|
||||
// running in an uninitialized state.
|
||||
initialize, _ := req.Options[initOptionKwd].(bool)
|
||||
if initialize {
|
||||
if initialize && !fsrepo.IsInitialized(cctx.ConfigRoot) {
|
||||
cfgLocation, _ := req.Options[initConfigOptionKwd].(string)
|
||||
profiles, _ := req.Options[initProfileOptionKwd].(string)
|
||||
var conf *config.Config
|
||||
|
||||
cfg := cctx.ConfigRoot
|
||||
if !fsrepo.IsInitialized(cfg) {
|
||||
profiles, _ := req.Options[initProfileOptionKwd].(string)
|
||||
|
||||
err := initWithDefaults(os.Stdout, cfg, profiles)
|
||||
if err != nil {
|
||||
if cfgLocation != "" {
|
||||
if conf, err = cserial.Load(cfgLocation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = doInit(os.Stdout, cctx.ConfigRoot, false, nBitsForKeypairDefault, profiles, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// acquire the repo lock _before_ constructing a node. we need to make
|
||||
@ -283,11 +293,6 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
// fail before we get to that. It can't hurt to close it twice.
|
||||
defer repo.Close()
|
||||
|
||||
cfg, err := cctx.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offline, _ := req.Options[offlineKwd].(bool)
|
||||
ipnsps, _ := req.Options[enableIPNSPubSubKwd].(bool)
|
||||
pubsub, _ := req.Options[enablePubSubKwd].(bool)
|
||||
@ -326,6 +331,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
ncfg.Routing = libp2p.DHTClientOption
|
||||
case routingOptionDHTKwd:
|
||||
ncfg.Routing = libp2p.DHTOption
|
||||
case routingOptionDHTServerKwd:
|
||||
ncfg.Routing = libp2p.DHTServerOption
|
||||
case routingOptionNoneKwd:
|
||||
ncfg.Routing = libp2p.NilRouterOption
|
||||
default:
|
||||
@ -364,11 +371,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
|
||||
// Start "core" plugins. We want to do this *before* starting the HTTP
|
||||
// API as the user may be relying on these plugins.
|
||||
api, err := coreapi.NewCoreAPI(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cctx.Plugins.Start(api)
|
||||
err = cctx.Plugins.Start(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -397,31 +400,41 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
return err
|
||||
}
|
||||
|
||||
// construct http gateway - if it is set in the config
|
||||
var gwErrc <-chan error
|
||||
if len(cfg.Addresses.Gateway) > 0 {
|
||||
var err error
|
||||
gwErrc, err = serveHTTPGateway(req, cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// construct http gateway
|
||||
gwErrc, err := serveHTTPGateway(req, cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add ipfs version info to prometheus metrics
|
||||
var ipfsInfoMetric = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "ipfs_info",
|
||||
Help: "IPFS version information.",
|
||||
}, []string{"version", "commit"})
|
||||
|
||||
// Setting to 1 lets us multiply it with other stats to add the version labels
|
||||
ipfsInfoMetric.With(prometheus.Labels{
|
||||
"version": version.CurrentVersionNumber,
|
||||
"commit": version.CurrentCommit,
|
||||
}).Set(1)
|
||||
|
||||
// initialize metrics collector
|
||||
prometheus.MustRegister(&corehttp.IpfsNodeCollector{Node: node})
|
||||
|
||||
// The daemon is *finally* ready.
|
||||
fmt.Printf("Daemon is ready\n")
|
||||
notifyReady()
|
||||
|
||||
// Give the user some immediate feedback when they hit C-c
|
||||
go func() {
|
||||
<-req.Context.Done()
|
||||
notifyStopping()
|
||||
fmt.Println("Received interrupt signal, shutting down...")
|
||||
fmt.Println("(Hit ctrl-c again to force-shutdown the daemon.)")
|
||||
}()
|
||||
|
||||
// collect long-running errors and block for shutdown
|
||||
// TODO(cryptix): our fuse currently doesnt follow this pattern for graceful shutdown
|
||||
// TODO(cryptix): our fuse currently doesn't follow this pattern for graceful shutdown
|
||||
var errs error
|
||||
for err := range merge(apiErrc, gwErrc, gcErrc) {
|
||||
if err != nil {
|
||||
@ -439,6 +452,11 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
|
||||
return nil, fmt.Errorf("serveHTTPApi: GetConfig() failed: %s", err)
|
||||
}
|
||||
|
||||
listeners, err := sockets.TakeListeners("io.ipfs.api")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPApi: socket activation failed: %s", err)
|
||||
}
|
||||
|
||||
apiAddrs := make([]string, 0, 2)
|
||||
apiAddr, _ := req.Options[commands.ApiOption].(string)
|
||||
if apiAddr == "" {
|
||||
@ -447,11 +465,18 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
|
||||
apiAddrs = append(apiAddrs, apiAddr)
|
||||
}
|
||||
|
||||
listeners := make([]manet.Listener, 0, len(apiAddrs))
|
||||
listenerAddrs := make(map[string]bool, len(listeners))
|
||||
for _, listener := range listeners {
|
||||
listenerAddrs[string(listener.Multiaddr().Bytes())] = true
|
||||
}
|
||||
|
||||
for _, addr := range apiAddrs {
|
||||
apiMaddr, err := ma.NewMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPApi: invalid API address: %q (err: %s)", apiAddr, err)
|
||||
return nil, fmt.Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err)
|
||||
}
|
||||
if listenerAddrs[string(apiMaddr.Bytes())] {
|
||||
continue
|
||||
}
|
||||
|
||||
apiLis, err := manet.Listen(apiMaddr)
|
||||
@ -459,13 +484,20 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
|
||||
return nil, fmt.Errorf("serveHTTPApi: manet.Listen(%s) failed: %s", apiMaddr, err)
|
||||
}
|
||||
|
||||
// we might have listened to /tcp/0 - lets see what we are listing on
|
||||
apiMaddr = apiLis.Multiaddr()
|
||||
fmt.Printf("API server listening on %s\n", apiMaddr)
|
||||
fmt.Printf("WebUI: http://%s/webui\n", apiLis.Addr())
|
||||
listenerAddrs[string(apiMaddr.Bytes())] = true
|
||||
listeners = append(listeners, apiLis)
|
||||
}
|
||||
|
||||
for _, listener := range listeners {
|
||||
// we might have listened to /tcp/0 - let's see what we are listing on
|
||||
fmt.Printf("API server listening on %s\n", listener.Multiaddr())
|
||||
// Browsers require TCP.
|
||||
switch listener.Addr().Network() {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
fmt.Printf("WebUI: http://%s/webui\n", listener.Addr())
|
||||
}
|
||||
}
|
||||
|
||||
// by default, we don't let you load arbitrary ipfs objects through the api,
|
||||
// because this would open up the api to scripting vulnerabilities.
|
||||
// only the webui objects are allowed.
|
||||
@ -564,36 +596,51 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
|
||||
writable = cfg.Gateway.Writable
|
||||
}
|
||||
|
||||
listeners, err := sockets.TakeListeners("io.ipfs.gateway")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPGateway: socket activation failed: %s", err)
|
||||
}
|
||||
|
||||
listenerAddrs := make(map[string]bool, len(listeners))
|
||||
for _, listener := range listeners {
|
||||
listenerAddrs[string(listener.Multiaddr().Bytes())] = true
|
||||
}
|
||||
|
||||
gatewayAddrs := cfg.Addresses.Gateway
|
||||
listeners := make([]manet.Listener, 0, len(gatewayAddrs))
|
||||
for _, addr := range gatewayAddrs {
|
||||
gatewayMaddr, err := ma.NewMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err)
|
||||
}
|
||||
|
||||
if listenerAddrs[string(gatewayMaddr.Bytes())] {
|
||||
continue
|
||||
}
|
||||
|
||||
gwLis, err := manet.Listen(gatewayMaddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPGateway: manet.Listen(%s) failed: %s", gatewayMaddr, err)
|
||||
}
|
||||
// we might have listened to /tcp/0 - lets see what we are listing on
|
||||
gatewayMaddr = gwLis.Multiaddr()
|
||||
|
||||
if writable {
|
||||
fmt.Printf("Gateway (writable) server listening on %s\n", gatewayMaddr)
|
||||
} else {
|
||||
fmt.Printf("Gateway (readonly) server listening on %s\n", gatewayMaddr)
|
||||
}
|
||||
|
||||
listenerAddrs[string(gatewayMaddr.Bytes())] = true
|
||||
listeners = append(listeners, gwLis)
|
||||
}
|
||||
|
||||
// we might have listened to /tcp/0 - let's see what we are listing on
|
||||
gwType := "readonly"
|
||||
if writable {
|
||||
gwType = "writable"
|
||||
}
|
||||
|
||||
for _, listener := range listeners {
|
||||
fmt.Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr())
|
||||
}
|
||||
|
||||
cmdctx := *cctx
|
||||
cmdctx.Gateway = true
|
||||
|
||||
var opts = []corehttp.ServeOption{
|
||||
corehttp.MetricsCollectionOption("gateway"),
|
||||
corehttp.IPNSHostnameOption(),
|
||||
corehttp.HostnameOption(),
|
||||
corehttp.GatewayOption(writable, "/ipfs", "/ipns"),
|
||||
corehttp.VersionOption(),
|
||||
corehttp.CheckVersionOption(),
|
||||
@ -601,7 +648,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
|
||||
}
|
||||
|
||||
if cfg.Experimental.P2pHttpProxy {
|
||||
opts = append(opts, corehttp.ProxyOption())
|
||||
opts = append(opts, corehttp.P2PProxyOption())
|
||||
}
|
||||
|
||||
if len(cfg.Gateway.RootRedirect) > 0 {
|
||||
@ -726,7 +773,11 @@ func YesNoPrompt(prompt string) bool {
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
fmt.Printf("go-ipfs version: %s-%s\n", version.CurrentVersionNumber, version.CurrentCommit)
|
||||
v := version.CurrentVersionNumber
|
||||
if version.CurrentCommit != "" {
|
||||
v += "-" + version.CurrentCommit
|
||||
}
|
||||
fmt.Printf("go-ipfs version: %s\n", v)
|
||||
fmt.Printf("Repo version: %d\n", fsrepo.RepoVersion)
|
||||
fmt.Printf("System version: %s\n", runtime.GOARCH+"/"+runtime.GOOS)
|
||||
fmt.Printf("Golang version: %s\n", runtime.Version())
|
||||
|
||||
15
cmd/ipfs/daemon_linux.go
Normal file
15
cmd/ipfs/daemon_linux.go
Normal file
@ -0,0 +1,15 @@
|
||||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
daemon "github.com/coreos/go-systemd/v22/daemon"
|
||||
)
|
||||
|
||||
func notifyReady() {
|
||||
_, _ = daemon.SdNotify(false, daemon.SdNotifyReady)
|
||||
}
|
||||
|
||||
func notifyStopping() {
|
||||
_, _ = daemon.SdNotify(false, daemon.SdNotifyStopping)
|
||||
}
|
||||
7
cmd/ipfs/daemon_other.go
Normal file
7
cmd/ipfs/daemon_other.go
Normal file
@ -0,0 +1,7 @@
|
||||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
func notifyReady() {}
|
||||
|
||||
func notifyStopping() {}
|
||||
27
cmd/ipfs/dist/LICENSE
vendored
27
cmd/ipfs/dist/LICENSE
vendored
@ -1,21 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
This project is transitioning from an MIT-only license to a dual MIT/Apache-2.0 license.
|
||||
Unless otherwise noted, all code contributed prior to 2019-05-06 and not contributed by
|
||||
a user listed in [this signoff issue](https://github.com/ipfs/go-ipfs/issues/6302) is
|
||||
licensed under MIT-only. All new contributions (and past contributions since 2019-05-06)
|
||||
are licensed under a dual MIT/Apache-2.0 license.
|
||||
|
||||
Copyright (c) 2014 Juan Batiz-Benet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
MIT: https://www.opensource.org/licenses/mit
|
||||
Apache-2.0: https://www.apache.org/licenses/license-2.0
|
||||
|
||||
5
cmd/ipfs/dist/LICENSE-APACHE
vendored
Normal file
5
cmd/ipfs/dist/LICENSE-APACHE
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
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.
|
||||
19
cmd/ipfs/dist/LICENSE-MIT
vendored
Normal file
19
cmd/ipfs/dist/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
2
cmd/ipfs/dist/install.sh
vendored
2
cmd/ipfs/dist/install.sh
vendored
@ -13,7 +13,7 @@ binpaths="/usr/local/bin /usr/bin"
|
||||
is_write_perm_missing=""
|
||||
|
||||
for binpath in $binpaths; do
|
||||
if mv "$bin" "$binpath/$bin" ; then
|
||||
if mv "$bin" "$binpath/ipfs" ; then
|
||||
echo "Moved $bin to $binpath"
|
||||
exit 0
|
||||
else
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
assets "github.com/ipfs/go-ipfs/assets"
|
||||
@ -16,9 +16,9 @@ import (
|
||||
namesys "github.com/ipfs/go-ipfs/namesys"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-ipfs-config"
|
||||
"github.com/ipfs/go-ipfs-files"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,6 +28,10 @@ const (
|
||||
profileOptionName = "profile"
|
||||
)
|
||||
|
||||
var errRepoExists = errors.New(`ipfs configuration file already exists!
|
||||
Reinitializing would overwrite your keys.
|
||||
`)
|
||||
|
||||
var initCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Initializes ipfs config file.",
|
||||
@ -102,31 +106,30 @@ environment variable:
|
||||
}
|
||||
}
|
||||
|
||||
profile, _ := req.Options[profileOptionName].(string)
|
||||
|
||||
var profiles []string
|
||||
if profile != "" {
|
||||
profiles = strings.Split(profile, ",")
|
||||
}
|
||||
|
||||
profiles, _ := req.Options[profileOptionName].(string)
|
||||
return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profiles, conf)
|
||||
},
|
||||
}
|
||||
|
||||
var errRepoExists = errors.New(`ipfs configuration file already exists!
|
||||
Reinitializing would overwrite your keys.
|
||||
`)
|
||||
|
||||
func initWithDefaults(out io.Writer, repoRoot string, profile string) error {
|
||||
var profiles []string
|
||||
if profile != "" {
|
||||
profiles = strings.Split(profile, ",")
|
||||
func applyProfiles(conf *config.Config, profiles string) error {
|
||||
if profiles == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return doInit(out, repoRoot, false, nBitsForKeypairDefault, profiles, nil)
|
||||
for _, profile := range strings.Split(profiles, ",") {
|
||||
transformer, ok := config.Profiles[profile]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid configuration profile: %s", profile)
|
||||
}
|
||||
|
||||
if err := transformer.Transform(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, confProfiles []string, conf *config.Config) error {
|
||||
func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, confProfiles string, conf *config.Config) error {
|
||||
if _, err := fmt.Fprintf(out, "initializing IPFS node at %s\n", repoRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -147,15 +150,8 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con
|
||||
}
|
||||
}
|
||||
|
||||
for _, profile := range confProfiles {
|
||||
transformer, ok := config.Profiles[profile]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid configuration profile: %s", profile)
|
||||
}
|
||||
|
||||
if err := transformer.Transform(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := applyProfiles(conf, confProfiles); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fsrepo.Init(repoRoot, conf); err != nil {
|
||||
@ -175,7 +171,7 @@ func checkWritable(dir string) error {
|
||||
_, err := os.Stat(dir)
|
||||
if err == nil {
|
||||
// dir exists, make sure we can write to it
|
||||
testfile := path.Join(dir, "test")
|
||||
testfile := filepath.Join(dir, "test")
|
||||
fi, err := os.Create(testfile)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
|
||||
268
cmd/ipfs/main.go
268
cmd/ipfs/main.go
@ -6,8 +6,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
@ -21,11 +22,10 @@ import (
|
||||
repo "github.com/ipfs/go-ipfs/repo"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
|
||||
osh "github.com/Kubuxu/go-os-helper"
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-ipfs-cmds/cli"
|
||||
"github.com/ipfs/go-ipfs-cmds/http"
|
||||
"github.com/ipfs/go-ipfs-config"
|
||||
cmdhttp "github.com/ipfs/go-ipfs-cmds/http"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
u "github.com/ipfs/go-ipfs-util"
|
||||
logging "github.com/ipfs/go-log"
|
||||
loggables "github.com/libp2p/go-libp2p-loggables"
|
||||
@ -47,18 +47,7 @@ const (
|
||||
)
|
||||
|
||||
func loadPlugins(repoPath string) (*loader.PluginLoader, error) {
|
||||
pluginpath := filepath.Join(repoPath, "plugins")
|
||||
|
||||
// check if repo is accessible before loading plugins
|
||||
var plugins *loader.PluginLoader
|
||||
ok, err := checkPermissions(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
pluginpath = ""
|
||||
}
|
||||
plugins, err = loader.NewPluginLoader(pluginpath)
|
||||
plugins, err := loader.NewPluginLoader(repoPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading plugins: %s", err)
|
||||
}
|
||||
@ -124,6 +113,9 @@ func mainRet() int {
|
||||
os.Args[1] = "--help"
|
||||
}
|
||||
}
|
||||
} else if insideGUI() { // if no args were passed, and we're in a GUI environment
|
||||
// launch the daemon instead of launching a ghost window
|
||||
os.Args = append(os.Args, "daemon", "--init")
|
||||
}
|
||||
|
||||
// output depends on executable name passed in os.Args
|
||||
@ -183,6 +175,10 @@ func mainRet() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func insideGUI() bool {
|
||||
return util.InsideGUI()
|
||||
}
|
||||
|
||||
func checkDebug(req *cmds.Request) {
|
||||
// check if user wants to debug. option OR env var.
|
||||
debug, _ := req.Options["debug"].(bool)
|
||||
@ -195,39 +191,114 @@ func checkDebug(req *cmds.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) {
|
||||
apiAddrStr, apiSpecified := req.Options[corecmds.ApiOption].(string)
|
||||
if !apiSpecified {
|
||||
return nil, nil
|
||||
}
|
||||
return ma.NewMultiaddr(apiAddrStr)
|
||||
}
|
||||
|
||||
func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
|
||||
exe := cmds.NewExecutor(req.Root)
|
||||
cctx := env.(*oldcmds.Context)
|
||||
details := commandDetails(req.Path)
|
||||
client, err := commandShouldRunOnDaemon(*details, req, env.(*oldcmds.Context))
|
||||
|
||||
// Check if the command is disabled.
|
||||
if details.cannotRunOnClient && details.cannotRunOnDaemon {
|
||||
return nil, fmt.Errorf("command disabled: %v", req.Path)
|
||||
}
|
||||
|
||||
// Can we just run this locally?
|
||||
if !details.cannotRunOnClient && details.doesNotUseRepo {
|
||||
return exe, nil
|
||||
}
|
||||
|
||||
// Get the API option from the commandline.
|
||||
apiAddr, err := apiAddrOption(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var exctr cmds.Executor
|
||||
if client != nil && !req.Command.External {
|
||||
exctr = client.(cmds.Executor)
|
||||
} else {
|
||||
exctr = cmds.NewExecutor(req.Root)
|
||||
// Require that the command be run on the daemon when the API flag is
|
||||
// passed (unless we're trying to _run_ the daemon).
|
||||
daemonRequested := apiAddr != nil && req.Command != daemonCmd
|
||||
|
||||
// Run this on the client if required.
|
||||
if details.cannotRunOnDaemon || req.Command.External {
|
||||
if daemonRequested {
|
||||
// User requested that the command be run on the daemon but we can't.
|
||||
// NOTE: We drop this check for the `ipfs daemon` command.
|
||||
return nil, errors.New("api flag specified but command cannot be run on the daemon")
|
||||
}
|
||||
return exe, nil
|
||||
}
|
||||
|
||||
return exctr, nil
|
||||
}
|
||||
|
||||
func checkPermissions(path string) (bool, error) {
|
||||
_, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
// repo does not exist yet - don't load plugins, but also don't fail
|
||||
return false, nil
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
// repo is not accessible. error out.
|
||||
return false, fmt.Errorf("error opening repository at %s: permission denied", path)
|
||||
// Finally, look in the repo for an API file.
|
||||
if apiAddr == nil {
|
||||
var err error
|
||||
apiAddr, err = fsrepo.APIAddr(cctx.ConfigRoot)
|
||||
switch err {
|
||||
case nil, repo.ErrApiNotRunning:
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
// Still no api specified? Run it on the client or fail.
|
||||
if apiAddr == nil {
|
||||
if details.cannotRunOnClient {
|
||||
return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path)
|
||||
}
|
||||
return exe, nil
|
||||
}
|
||||
|
||||
// Resolve the API addr.
|
||||
apiAddr, err = resolveAddr(req.Context, apiAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network, host, err := manet.DialArgs(apiAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Construct the executor.
|
||||
opts := []cmdhttp.ClientOpt{
|
||||
cmdhttp.ClientWithAPIPrefix(corehttp.APIPath),
|
||||
}
|
||||
|
||||
// Fallback on a local executor if we (a) have a repo and (b) aren't
|
||||
// forcing a daemon.
|
||||
if !daemonRequested && fsrepo.IsInitialized(cctx.ConfigRoot) {
|
||||
opts = append(opts, cmdhttp.ClientWithFallback(exe))
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
case "unix":
|
||||
path := host
|
||||
host = "unix"
|
||||
opts = append(opts, cmdhttp.ClientWithHTTPClient(&http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", path)
|
||||
},
|
||||
},
|
||||
}))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported API address: %s", apiAddr)
|
||||
}
|
||||
|
||||
return cmdhttp.NewClient(host, opts...), nil
|
||||
}
|
||||
|
||||
// commandDetails returns a command's details for the command given by |path|.
|
||||
func commandDetails(path []string) *cmdDetails {
|
||||
func commandDetails(path []string) cmdDetails {
|
||||
if len(path) == 0 {
|
||||
// special case root command
|
||||
return cmdDetails{doesNotUseRepo: true}
|
||||
}
|
||||
var details cmdDetails
|
||||
// find the last command in path that has a cmdDetailsMap entry
|
||||
for i := range path {
|
||||
@ -235,67 +306,7 @@ func commandDetails(path []string) *cmdDetails {
|
||||
details = cmdDetails
|
||||
}
|
||||
}
|
||||
return &details
|
||||
}
|
||||
|
||||
// commandShouldRunOnDaemon determines, from command details, whether a
|
||||
// command ought to be executed on an ipfs daemon.
|
||||
//
|
||||
// It returns a client if the command should be executed on a daemon and nil if
|
||||
// it should be executed on a client. It returns an error if the command must
|
||||
// NOT be executed on either.
|
||||
func commandShouldRunOnDaemon(details cmdDetails, req *cmds.Request, cctx *oldcmds.Context) (http.Client, error) {
|
||||
path := req.Path
|
||||
// root command.
|
||||
if len(path) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if details.cannotRunOnClient && details.cannotRunOnDaemon {
|
||||
return nil, fmt.Errorf("command disabled: %s", path[0])
|
||||
}
|
||||
|
||||
if details.doesNotUseRepo && details.canRunOnClient() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// at this point need to know whether api is running. we defer
|
||||
// to this point so that we don't check unnecessarily
|
||||
|
||||
// did user specify an api to use for this command?
|
||||
apiAddrStr, _ := req.Options[corecmds.ApiOption].(string)
|
||||
|
||||
client, err := getAPIClient(req.Context, cctx.ConfigRoot, apiAddrStr)
|
||||
if err == repo.ErrApiNotRunning {
|
||||
if apiAddrStr != "" && req.Command != daemonCmd {
|
||||
// if user SPECIFIED an api, and this cmd is not daemon
|
||||
// we MUST use it. so error out.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ok for api not to be running
|
||||
} else if err != nil { // some other api error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if client != nil {
|
||||
if details.cannotRunOnDaemon {
|
||||
// check if daemon locked. legacy error text, for now.
|
||||
log.Debugf("Command cannot run on daemon. Checking if daemon is locked")
|
||||
if daemonLocked, _ := fsrepo.LockedByOtherProcess(cctx.ConfigRoot); daemonLocked {
|
||||
return nil, cmds.ClientError("ipfs daemon is running. please stop it to run this command")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
if details.cannotRunOnClient {
|
||||
return nil, cmds.ClientError("must run on the ipfs daemon")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return details
|
||||
}
|
||||
|
||||
func getRepoPath(req *cmds.Request) (string, error) {
|
||||
@ -366,67 +377,6 @@ func profileIfEnabled() (func(), error) {
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
var apiFileErrorFmt string = `Failed to parse '%[1]s/api' file.
|
||||
error: %[2]s
|
||||
If you're sure go-ipfs isn't running, you can just delete it.
|
||||
`
|
||||
var checkIPFSUnixFmt = "Otherwise check:\n\tps aux | grep ipfs"
|
||||
var checkIPFSWinFmt = "Otherwise check:\n\ttasklist | findstr ipfs"
|
||||
|
||||
// getAPIClient checks the repo, and the given options, checking for
|
||||
// a running API service. if there is one, it returns a client.
|
||||
// otherwise, it returns errApiNotRunning, or another error.
|
||||
func getAPIClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client, error) {
|
||||
var apiErrorFmt string
|
||||
switch {
|
||||
case osh.IsUnix():
|
||||
apiErrorFmt = apiFileErrorFmt + checkIPFSUnixFmt
|
||||
case osh.IsWindows():
|
||||
apiErrorFmt = apiFileErrorFmt + checkIPFSWinFmt
|
||||
default:
|
||||
apiErrorFmt = apiFileErrorFmt
|
||||
}
|
||||
|
||||
var addr ma.Multiaddr
|
||||
var err error
|
||||
if len(apiAddrStr) != 0 {
|
||||
addr, err = ma.NewMultiaddr(apiAddrStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addr.Protocols()) == 0 {
|
||||
return nil, fmt.Errorf("multiaddr doesn't provide any protocols")
|
||||
}
|
||||
} else {
|
||||
addr, err = fsrepo.APIAddr(repoPath)
|
||||
if err == repo.ErrApiNotRunning {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(apiErrorFmt, repoPath, err.Error())
|
||||
}
|
||||
}
|
||||
if len(addr.Protocols()) == 0 {
|
||||
return nil, fmt.Errorf(apiErrorFmt, repoPath, "multiaddr doesn't provide any protocols")
|
||||
}
|
||||
return apiClientForAddr(ctx, addr)
|
||||
}
|
||||
|
||||
func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) {
|
||||
addr, err := resolveAddr(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, host, err := manet.DialArgs(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
|
||||
}
|
||||
|
||||
func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) {
|
||||
ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancelFunc()
|
||||
|
||||
7
cmd/ipfs/util/ui.go
Normal file
7
cmd/ipfs/util/ui.go
Normal file
@ -0,0 +1,7 @@
|
||||
//+build !windows
|
||||
|
||||
package util
|
||||
|
||||
func InsideGUI() bool {
|
||||
return false
|
||||
}
|
||||
18
cmd/ipfs/util/ui_windows.go
Normal file
18
cmd/ipfs/util/ui_windows.go
Normal file
@ -0,0 +1,18 @@
|
||||
package util
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
func InsideGUI() bool {
|
||||
conhostInfo := &windows.ConsoleScreenBufferInfo{}
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Stdout, conhostInfo); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if (conhostInfo.CursorPosition.X | conhostInfo.CursorPosition.Y) == 0 {
|
||||
// console cursor has not moved prior to our execution
|
||||
// high probability that we're not in a terminal
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -66,8 +66,8 @@ func ManageFdLimit() (changed bool, newLimit uint64, err error) {
|
||||
// the soft limit is the value that the kernel enforces for the
|
||||
// corresponding resource
|
||||
// the hard limit acts as a ceiling for the soft limit
|
||||
// an unprivileged process may only set it's soft limit to a
|
||||
// alue in the range from 0 up to the hard limit
|
||||
// an unprivileged process may only set its soft limit to a
|
||||
// value in the range from 0 up to the hard limit
|
||||
err = setLimit(targetLimit, targetLimit)
|
||||
switch err {
|
||||
case nil:
|
||||
@ -82,7 +82,7 @@ func ManageFdLimit() (changed bool, newLimit uint64, err error) {
|
||||
// set the soft value
|
||||
err = setLimit(targetLimit, hard)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error setting ulimit wihout hard limit: %s", err)
|
||||
err = fmt.Errorf("error setting ulimit without hard limit: %s", err)
|
||||
break
|
||||
}
|
||||
newLimit = targetLimit
|
||||
|
||||
@ -10,7 +10,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -20,9 +19,9 @@ import (
|
||||
"syscall"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
ci "github.com/libp2p/go-libp2p-crypto"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem"
|
||||
secio "github.com/libp2p/go-libp2p-secio"
|
||||
)
|
||||
@ -113,8 +112,8 @@ func main() {
|
||||
}
|
||||
|
||||
func setupPeer(a args) (peer.ID, pstore.Peerstore, error) {
|
||||
if a.keybits < 1024 {
|
||||
return "", nil, errors.New("bitsize less than 1024 is considered unsafe")
|
||||
if a.keybits < ci.MinRsaKeyBits {
|
||||
return "", nil, ci.ErrRsaKeyTooSmall
|
||||
}
|
||||
|
||||
out("generating key pair...")
|
||||
|
||||
@ -57,6 +57,12 @@ func (c *Context) GetNode() (*core.IpfsNode, error) {
|
||||
return nil, errors.New("nil ConstructNode function")
|
||||
}
|
||||
c.node, err = c.ConstructNode()
|
||||
if err == nil {
|
||||
// Pre-load the config from the repo to avoid re-parsing it from disk.
|
||||
if cfg, err := c.node.Repo.Config(); err != nil {
|
||||
c.config = cfg
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.node, err
|
||||
}
|
||||
|
||||
@ -9,17 +9,15 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/jbenet/goprocess"
|
||||
"github.com/jbenet/goprocess/context"
|
||||
"github.com/jbenet/goprocess/periodic"
|
||||
"github.com/libp2p/go-libp2p-host"
|
||||
"github.com/libp2p/go-libp2p-loggables"
|
||||
"github.com/libp2p/go-libp2p-net"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/libp2p/go-libp2p-peerstore"
|
||||
"github.com/libp2p/go-libp2p-routing"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-core/peerstore"
|
||||
"github.com/libp2p/go-libp2p-core/routing"
|
||||
)
|
||||
|
||||
var log = logging.Logger("bootstrap")
|
||||
@ -51,7 +49,7 @@ type BootstrapConfig struct {
|
||||
// BootstrapPeers is a function that returns a set of bootstrap peers
|
||||
// for the bootstrap process to use. This makes it possible for clients
|
||||
// to control the peers the process uses at any moment.
|
||||
BootstrapPeers func() []peerstore.PeerInfo
|
||||
BootstrapPeers func() []peer.AddrInfo
|
||||
}
|
||||
|
||||
// DefaultBootstrapConfig specifies default sane parameters for bootstrapping.
|
||||
@ -61,9 +59,9 @@ var DefaultBootstrapConfig = BootstrapConfig{
|
||||
ConnectionTimeout: (30 * time.Second) / 3, // Perod / 3
|
||||
}
|
||||
|
||||
func BootstrapConfigWithPeers(pis []peerstore.PeerInfo) BootstrapConfig {
|
||||
func BootstrapConfigWithPeers(pis []peer.AddrInfo) BootstrapConfig {
|
||||
cfg := DefaultBootstrapConfig
|
||||
cfg.BootstrapPeers = func() []peerstore.PeerInfo {
|
||||
cfg.BootstrapPeers = func() []peer.AddrInfo {
|
||||
return pis
|
||||
}
|
||||
return cfg
|
||||
@ -73,7 +71,7 @@ func BootstrapConfigWithPeers(pis []peerstore.PeerInfo) BootstrapConfig {
|
||||
// check the number of open connections and -- if there are too few -- initiate
|
||||
// connections to well-known bootstrap peers. It also kicks off subsystem
|
||||
// bootstrapping (i.e. routing).
|
||||
func Bootstrap(id peer.ID, host host.Host, rt routing.IpfsRouting, cfg BootstrapConfig) (io.Closer, error) {
|
||||
func Bootstrap(id peer.ID, host host.Host, rt routing.Routing, cfg BootstrapConfig) (io.Closer, error) {
|
||||
|
||||
// make a signal to wait for one bootstrap round to complete.
|
||||
doneWithRound := make(chan struct{})
|
||||
@ -81,16 +79,14 @@ func Bootstrap(id peer.ID, host host.Host, rt routing.IpfsRouting, cfg Bootstrap
|
||||
if len(cfg.BootstrapPeers()) == 0 {
|
||||
// We *need* to bootstrap but we have no bootstrap peers
|
||||
// configured *at all*, inform the user.
|
||||
log.Warning("no bootstrap nodes configured: go-ipfs may have difficulty connecting to the network")
|
||||
log.Warn("no bootstrap nodes configured: go-ipfs may have difficulty connecting to the network")
|
||||
}
|
||||
|
||||
// the periodic bootstrap function -- the connection supervisor
|
||||
periodic := func(worker goprocess.Process) {
|
||||
ctx := goprocessctx.OnClosingContext(worker)
|
||||
defer log.EventBegin(ctx, "periodicBootstrap", id).Done()
|
||||
|
||||
if err := bootstrapRound(ctx, host, cfg); err != nil {
|
||||
log.Event(ctx, "bootstrapError", id, loggables.Error(err))
|
||||
log.Debugf("%s bootstrap error: %s", id, err)
|
||||
}
|
||||
|
||||
@ -127,7 +123,6 @@ func bootstrapRound(ctx context.Context, host host.Host, cfg BootstrapConfig) er
|
||||
// determine how many bootstrap connections to open
|
||||
connected := host.Network().Peers()
|
||||
if len(connected) >= cfg.MinPeerThreshold {
|
||||
log.Event(ctx, "bootstrapSkip", id)
|
||||
log.Debugf("%s core bootstrap skipped -- connected to %d (> %d) nodes",
|
||||
id, len(connected), cfg.MinPeerThreshold)
|
||||
return nil
|
||||
@ -135,9 +130,9 @@ func bootstrapRound(ctx context.Context, host host.Host, cfg BootstrapConfig) er
|
||||
numToDial := cfg.MinPeerThreshold - len(connected)
|
||||
|
||||
// filter out bootstrap nodes we are already connected to
|
||||
var notConnected []peerstore.PeerInfo
|
||||
var notConnected []peer.AddrInfo
|
||||
for _, p := range peers {
|
||||
if host.Network().Connectedness(p.ID) != net.Connected {
|
||||
if host.Network().Connectedness(p.ID) != network.Connected {
|
||||
notConnected = append(notConnected, p)
|
||||
}
|
||||
}
|
||||
@ -151,12 +146,11 @@ func bootstrapRound(ctx context.Context, host host.Host, cfg BootstrapConfig) er
|
||||
// connect to a random susbset of bootstrap candidates
|
||||
randSubset := randomSubsetOfPeers(notConnected, numToDial)
|
||||
|
||||
defer log.EventBegin(ctx, "bootstrapStart", id).Done()
|
||||
log.Debugf("%s bootstrapping to %d nodes: %s", id, numToDial, randSubset)
|
||||
return bootstrapConnect(ctx, host, randSubset)
|
||||
}
|
||||
|
||||
func bootstrapConnect(ctx context.Context, ph host.Host, peers []peerstore.PeerInfo) error {
|
||||
func bootstrapConnect(ctx context.Context, ph host.Host, peers []peer.AddrInfo) error {
|
||||
if len(peers) < 1 {
|
||||
return ErrNotEnoughBootstrapPeers
|
||||
}
|
||||
@ -171,19 +165,16 @@ func bootstrapConnect(ctx context.Context, ph host.Host, peers []peerstore.PeerI
|
||||
// Also, performed asynchronously for dial speed.
|
||||
|
||||
wg.Add(1)
|
||||
go func(p peerstore.PeerInfo) {
|
||||
go func(p peer.AddrInfo) {
|
||||
defer wg.Done()
|
||||
defer log.EventBegin(ctx, "bootstrapDial", ph.ID(), p.ID).Done()
|
||||
log.Debugf("%s bootstrapping to %s", ph.ID(), p.ID)
|
||||
|
||||
ph.Peerstore().AddAddrs(p.ID, p.Addrs, peerstore.PermanentAddrTTL)
|
||||
if err := ph.Connect(ctx, p); err != nil {
|
||||
log.Event(ctx, "bootstrapDialFailed", p.ID)
|
||||
log.Debugf("failed to bootstrap with %v: %s", p.ID, err)
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
log.Event(ctx, "bootstrapDialSuccess", p.ID)
|
||||
log.Infof("bootstrapped with %v", p.ID)
|
||||
}(p)
|
||||
}
|
||||
@ -205,37 +196,14 @@ func bootstrapConnect(ctx context.Context, ph host.Host, peers []peerstore.PeerI
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomSubsetOfPeers(in []peerstore.PeerInfo, max int) []peerstore.PeerInfo {
|
||||
func randomSubsetOfPeers(in []peer.AddrInfo, max int) []peer.AddrInfo {
|
||||
if max > len(in) {
|
||||
max = len(in)
|
||||
}
|
||||
|
||||
out := make([]peerstore.PeerInfo, max)
|
||||
out := make([]peer.AddrInfo, max)
|
||||
for i, val := range rand.Perm(len(in))[:max] {
|
||||
out[i] = in[val]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type Peers []config.BootstrapPeer
|
||||
|
||||
func (bpeers Peers) ToPeerInfos() []peerstore.PeerInfo {
|
||||
pinfos := make(map[peer.ID]*peerstore.PeerInfo)
|
||||
for _, bootstrap := range bpeers {
|
||||
pinfo, ok := pinfos[bootstrap.ID()]
|
||||
if !ok {
|
||||
pinfo = new(peerstore.PeerInfo)
|
||||
pinfos[bootstrap.ID()] = pinfo
|
||||
pinfo.ID = bootstrap.ID()
|
||||
}
|
||||
|
||||
pinfo.Addrs = append(pinfo.Addrs, bootstrap.Transport())
|
||||
}
|
||||
|
||||
var peers []peerstore.PeerInfo
|
||||
for _, pinfo := range pinfos {
|
||||
peers = append(peers, *pinfo)
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
@ -1,56 +1,25 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
testutil "github.com/libp2p/go-testutil"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-core/test"
|
||||
)
|
||||
|
||||
func TestSubsetWhenMaxIsGreaterThanLengthOfSlice(t *testing.T) {
|
||||
var ps []pstore.PeerInfo
|
||||
var ps []peer.AddrInfo
|
||||
sizeofSlice := 100
|
||||
for i := 0; i < sizeofSlice; i++ {
|
||||
pid, err := testutil.RandPeerID()
|
||||
pid, err := test.RandPeerID()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ps = append(ps, pstore.PeerInfo{ID: pid})
|
||||
ps = append(ps, peer.AddrInfo{ID: pid})
|
||||
}
|
||||
out := randomSubsetOfPeers(ps, 2*sizeofSlice)
|
||||
if len(out) != len(ps) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleAddrsPerPeer(t *testing.T) {
|
||||
var bsps []config.BootstrapPeer
|
||||
for i := 0; i < 10; i++ {
|
||||
pid, err := testutil.RandPeerID()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("/ip4/127.0.0.1/tcp/5001/ipfs/%s", pid.Pretty())
|
||||
bsp1, err := config.ParseBootstrapPeer(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr = fmt.Sprintf("/ip4/127.0.0.1/udp/5002/utp/ipfs/%s", pid.Pretty())
|
||||
bsp2, err := config.ParseBootstrapPeer(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bsps = append(bsps, bsp1, bsp2)
|
||||
}
|
||||
|
||||
pinfos := Peers.ToPeerInfos(bsps)
|
||||
if len(pinfos) != len(bsps)/2 {
|
||||
t.Fatal("expected fewer peers")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,35 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/ipfs/go-metrics-interface"
|
||||
"go.uber.org/fx"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/bootstrap"
|
||||
"github.com/ipfs/go-ipfs/core/node"
|
||||
|
||||
"github.com/ipfs/go-metrics-interface"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
// from https://stackoverflow.com/a/59348871
|
||||
type valueContext struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (valueContext) Deadline() (deadline time.Time, ok bool) { return }
|
||||
func (valueContext) Done() <-chan struct{} { return nil }
|
||||
func (valueContext) Err() error { return nil }
|
||||
|
||||
type BuildCfg = node.BuildCfg // Alias for compatibility until we properly refactor the constructor interface
|
||||
|
||||
// NewNode constructs and returns an IpfsNode using the given cfg.
|
||||
func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
|
||||
// save this context as the "lifetime" ctx.
|
||||
lctx := ctx
|
||||
|
||||
// derive a new context that ignores cancellations from the lifetime ctx.
|
||||
ctx, cancel := context.WithCancel(valueContext{ctx})
|
||||
|
||||
// add a metrics scope.
|
||||
ctx = metrics.CtxScope(ctx, "ipfs")
|
||||
|
||||
n := &IpfsNode{
|
||||
@ -33,18 +50,27 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
|
||||
n.stop = func() error {
|
||||
once.Do(func() {
|
||||
stopErr = app.Stop(context.Background())
|
||||
if stopErr != nil {
|
||||
log.Error("failure on stop: ", stopErr)
|
||||
}
|
||||
// Cancel the context _after_ the app has stopped.
|
||||
cancel()
|
||||
})
|
||||
return stopErr
|
||||
}
|
||||
n.IsOnline = cfg.Online
|
||||
|
||||
go func() {
|
||||
// Note that some services use contexts to signal shutting down, which is
|
||||
// very suboptimal. This needs to be here until that's addressed somehow
|
||||
<-ctx.Done()
|
||||
err := n.stop()
|
||||
if err != nil {
|
||||
log.Error("failure on stop: ", err)
|
||||
// Shut down the application if the lifetime context is canceled.
|
||||
// NOTE: we _should_ stop the application by calling `Close()`
|
||||
// on the process. But we currently manage everything with contexts.
|
||||
select {
|
||||
case <-lctx.Done():
|
||||
err := n.stop()
|
||||
if err != nil {
|
||||
log.Error("failure on stop: ", err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-ipfs-files"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
"github.com/ipfs/interface-go-ipfs-core/options"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
@ -80,9 +80,9 @@ how to break files into blocks. Blocks with same content can
|
||||
be deduplicated. Different chunking strategies will produce different
|
||||
hashes for the same file. The default is a fixed block size of
|
||||
256 * 1024 bytes, 'size-262144'. Alternatively, you can use the
|
||||
Rabin fingerprint chunker for content defined chunking by specifying
|
||||
rabin-[min]-[avg]-[max] (where min/avg/max refer to the desired
|
||||
chunk sizes in bytes), e.g. 'rabin-262144-524288-1048576'.
|
||||
Buzhash or Rabin fingerprint chunker for content defined chunking by
|
||||
specifying buzhash or rabin-[min]-[avg]-[max] (where min/avg/max refer
|
||||
to the desired chunk sizes in bytes), e.g. 'rabin-262144-524288-1048576'.
|
||||
|
||||
The following examples use very small byte sizes to demonstrate the
|
||||
properties of the different chunkers on a small file. You'll likely
|
||||
@ -102,6 +102,11 @@ You can now check what blocks have been created by:
|
||||
QmY6yj1GsermExDXoosVE3aSPxdMNYr6aKuw3nA8LoWPRS 2059
|
||||
QmerURi9k4XzKCaaPbsK6BL5pMEjF7PGphjDvkkjDtsVf3 868
|
||||
QmQB28iwSriSUSMqG2nXDTLtdPHgWb4rebBrU7Q1j4vxPv 338
|
||||
|
||||
Finally, a note on hash determinism. While not guaranteed, adding the same
|
||||
file/directory with the same flags will almost always result in the same output
|
||||
hash. However, almost all of the flags provided by this command (other than pin,
|
||||
only-hash, and progress/status related flags) will change the final hash.
|
||||
`,
|
||||
},
|
||||
|
||||
@ -113,6 +118,8 @@ You can now check what blocks have been created by:
|
||||
cmds.OptionDerefArgs, // a builtin option that resolves passed in filesystem links (--dereference-args)
|
||||
cmds.OptionStdinName, // a builtin option that optionally allows wrapping stdin into a named file
|
||||
cmds.OptionHidden,
|
||||
cmds.OptionIgnore,
|
||||
cmds.OptionIgnoreRules,
|
||||
cmds.BoolOption(quietOptionName, "q", "Write minimal output."),
|
||||
cmds.BoolOption(quieterOptionName, "Q", "Write only final hash."),
|
||||
cmds.BoolOption(silentOptionName, "Write no output."),
|
||||
@ -120,7 +127,7 @@ You can now check what blocks have been created by:
|
||||
cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation."),
|
||||
cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk."),
|
||||
cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object."),
|
||||
cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes] or rabin-[min]-[avg]-[max]").WithDefault("size-262144"),
|
||||
cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes], rabin-[min]-[avg]-[max] or buzhash").WithDefault("size-262144"),
|
||||
cmds.BoolOption(pinOptionName, "Pin this object when adding.").WithDefault(true),
|
||||
cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes. (experimental)"),
|
||||
cmds.BoolOption(noCopyOptionName, "Add the file using filestore. Implies raw-leaves. (experimental)"),
|
||||
@ -286,7 +293,7 @@ You can now check what blocks have been created by:
|
||||
go func() {
|
||||
size, err := req.Files.Size()
|
||||
if err != nil {
|
||||
log.Warningf("error getting files size: %s", err)
|
||||
log.Warnf("error getting files size: %s", err)
|
||||
// see comment above
|
||||
return
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
decision "github.com/ipfs/go-bitswap/decision"
|
||||
cidutil "github.com/ipfs/go-cidutil"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
var BitswapCmd = &cmds.Command{
|
||||
@ -60,7 +60,7 @@ Print out all blocks currently on the bitswap wantlist for the local peer.`,
|
||||
|
||||
pstr, found := req.Options[peerOptionName].(string)
|
||||
if found {
|
||||
pid, err := peer.IDB58Decode(pstr)
|
||||
pid, err := peer.Decode(pstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -195,7 +195,7 @@ prints the ledger associated with a given peer.
|
||||
return e.TypeErr(bs, nd.Exchange)
|
||||
}
|
||||
|
||||
partner, err := peer.IDB58Decode(req.Arguments[0])
|
||||
partner, err := peer.Decode(req.Arguments[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -233,7 +233,7 @@ Trigger reprovider to announce our data to network.
|
||||
return ErrNotOnline
|
||||
}
|
||||
|
||||
err = nd.Reprovider.Trigger(req.Context)
|
||||
err = nd.Provider.Reprovide(req.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
|
||||
util "github.com/ipfs/go-ipfs/blocks/blockstoreutil"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
|
||||
@ -129,7 +131,7 @@ other than 'sha2-256' or format to anything other than 'v0' will result in CIDv1
|
||||
},
|
||||
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.FileArg("data", true, false, "The data to be stored as an IPFS block.").EnableStdin(),
|
||||
cmds.FileArg("data", true, true, "The data to be stored as an IPFS block.").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption(blockFormatOptionName, "f", "cid format for blocks to be created with."),
|
||||
@ -143,11 +145,6 @@ other than 'sha2-256' or format to anything other than 'v0' will result in CIDv1
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := cmdenv.GetFileArg(req.Files.Entries())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mhtype, _ := req.Options[mhtypeOptionName].(string)
|
||||
mhtval, ok := mh.Names[mhtype]
|
||||
if !ok {
|
||||
@ -170,18 +167,31 @@ other than 'sha2-256' or format to anything other than 'v0' will result in CIDv1
|
||||
|
||||
pin, _ := req.Options[pinOptionName].(bool)
|
||||
|
||||
p, err := api.Block().Put(req.Context, file,
|
||||
options.Block.Hash(mhtval, mhlen),
|
||||
options.Block.Format(format),
|
||||
options.Block.Pin(pin))
|
||||
if err != nil {
|
||||
return err
|
||||
it := req.Files.Entries()
|
||||
for it.Next() {
|
||||
file := files.FileFromEntry(it)
|
||||
if file == nil {
|
||||
return errors.New("expected a file")
|
||||
}
|
||||
|
||||
p, err := api.Block().Put(req.Context, file,
|
||||
options.Block.Hash(mhtval, mhlen),
|
||||
options.Block.Format(format),
|
||||
options.Block.Pin(pin))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = res.Emit(&BlockStat{
|
||||
Key: p.Path().Cid().String(),
|
||||
Size: p.Size(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &BlockStat{
|
||||
Key: p.Path().Cid().String(),
|
||||
Size: p.Size(),
|
||||
})
|
||||
return it.Err()
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error {
|
||||
|
||||
@ -2,6 +2,7 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
@ -11,6 +12,8 @@ import (
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
type BootstrapOutput struct {
|
||||
@ -64,26 +67,13 @@ in the bootstrap list).
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
deflt, _ := req.Options[defaultOptionName].(bool)
|
||||
|
||||
var inputPeers []config.BootstrapPeer
|
||||
if deflt {
|
||||
// parse separately for meaningful, correct error.
|
||||
defltPeers, err := config.DefaultBootstrapPeers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputPeers = defltPeers
|
||||
} else {
|
||||
inputPeers := config.DefaultBootstrapAddresses
|
||||
if !deflt {
|
||||
if err := req.ParseBodyArgs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedPeers, err := config.ParseBootstrapPeers(req.Arguments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputPeers = parsedPeers
|
||||
inputPeers = req.Arguments
|
||||
}
|
||||
|
||||
if len(inputPeers) == 0 {
|
||||
@ -110,7 +100,7 @@ in the bootstrap list).
|
||||
return err
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{config.BootstrapPeerStrings(added)})
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{added})
|
||||
},
|
||||
Type: BootstrapOutput{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
@ -127,11 +117,6 @@ var bootstrapAddDefaultCmd = &cmds.Command{
|
||||
in the bootstrap list).`,
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
defltPeers, err := config.DefaultBootstrapPeers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgRoot, err := cmdenv.GetConfigRoot(env)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -148,12 +133,12 @@ in the bootstrap list).`,
|
||||
return err
|
||||
}
|
||||
|
||||
added, err := bootstrapAdd(r, cfg, defltPeers)
|
||||
added, err := bootstrapAdd(r, cfg, config.DefaultBootstrapAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{config.BootstrapPeerStrings(added)})
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{added})
|
||||
},
|
||||
Type: BootstrapOutput{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
@ -201,26 +186,20 @@ var bootstrapRemoveCmd = &cmds.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
var removed []config.BootstrapPeer
|
||||
var removed []string
|
||||
if all {
|
||||
removed, err = bootstrapRemoveAll(r, cfg)
|
||||
} else {
|
||||
if err := req.ParseBodyArgs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
input, perr := config.ParseBootstrapPeers(req.Arguments)
|
||||
if perr != nil {
|
||||
return perr
|
||||
}
|
||||
|
||||
removed, err = bootstrapRemove(r, cfg, input)
|
||||
removed, err = bootstrapRemove(r, cfg, req.Arguments)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{config.BootstrapPeerStrings(removed)})
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{removed})
|
||||
},
|
||||
Type: BootstrapOutput{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
@ -257,7 +236,7 @@ var bootstrapRemoveAllCmd = &cmds.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{config.BootstrapPeerStrings(removed)})
|
||||
return cmds.EmitOnce(res, &BootstrapOutput{removed})
|
||||
},
|
||||
Type: BootstrapOutput{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
@ -315,23 +294,36 @@ func bootstrapWritePeers(w io.Writer, prefix string, peers []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []config.BootstrapPeer) ([]config.BootstrapPeer, error) {
|
||||
func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []string) ([]string, error) {
|
||||
for _, p := range peers {
|
||||
m, err := ma.NewMultiaddr(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tpt, p2ppart := ma.SplitLast(m)
|
||||
if p2ppart == nil || p2ppart.Protocol().Code != ma.P_P2P {
|
||||
return nil, fmt.Errorf("invalid bootstrap address: %s", p)
|
||||
}
|
||||
if tpt == nil {
|
||||
return nil, fmt.Errorf("bootstrap address without a transport: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
addedMap := map[string]struct{}{}
|
||||
addedList := make([]config.BootstrapPeer, 0, len(peers))
|
||||
addedList := make([]string, 0, len(peers))
|
||||
|
||||
// re-add cfg bootstrap peers to rm dupes
|
||||
bpeers := cfg.Bootstrap
|
||||
cfg.Bootstrap = nil
|
||||
|
||||
// add new peers
|
||||
for _, peer := range peers {
|
||||
s := peer.String()
|
||||
for _, s := range peers {
|
||||
if _, found := addedMap[s]; found {
|
||||
continue
|
||||
}
|
||||
|
||||
cfg.Bootstrap = append(cfg.Bootstrap, s)
|
||||
addedList = append(addedList, peer)
|
||||
addedList = append(addedList, s)
|
||||
addedMap[s] = struct{}{}
|
||||
}
|
||||
|
||||
@ -352,27 +344,56 @@ func bootstrapAdd(r repo.Repo, cfg *config.Config, peers []config.BootstrapPeer)
|
||||
return addedList, nil
|
||||
}
|
||||
|
||||
func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.BootstrapPeer) ([]config.BootstrapPeer, error) {
|
||||
removed := make([]config.BootstrapPeer, 0, len(toRemove))
|
||||
keep := make([]config.BootstrapPeer, 0, len(cfg.Bootstrap))
|
||||
func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []string) ([]string, error) {
|
||||
removed := make([]peer.AddrInfo, 0, len(toRemove))
|
||||
keep := make([]peer.AddrInfo, 0, len(cfg.Bootstrap))
|
||||
|
||||
toRemoveAddr, err := config.ParseBootstrapPeers(toRemove)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toRemoveMap := make(map[peer.ID][]ma.Multiaddr, len(toRemoveAddr))
|
||||
for _, addr := range toRemoveAddr {
|
||||
toRemoveMap[addr.ID] = addr.Addrs
|
||||
}
|
||||
|
||||
peers, err := cfg.BootstrapPeers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, peer := range peers {
|
||||
found := false
|
||||
for _, peer2 := range toRemove {
|
||||
if peer.Equal(peer2) {
|
||||
found = true
|
||||
removed = append(removed, peer)
|
||||
break
|
||||
for _, p := range peers {
|
||||
addrs, ok := toRemoveMap[p.ID]
|
||||
// not in the remove set?
|
||||
if !ok {
|
||||
keep = append(keep, p)
|
||||
continue
|
||||
}
|
||||
// remove the entire peer?
|
||||
if len(addrs) == 0 {
|
||||
removed = append(removed, p)
|
||||
continue
|
||||
}
|
||||
var (
|
||||
keptAddrs, removedAddrs []ma.Multiaddr
|
||||
)
|
||||
// remove specific addresses
|
||||
filter:
|
||||
for _, addr := range p.Addrs {
|
||||
for _, addr2 := range addrs {
|
||||
if addr.Equal(addr2) {
|
||||
removedAddrs = append(removedAddrs, addr)
|
||||
continue filter
|
||||
}
|
||||
}
|
||||
keptAddrs = append(keptAddrs, addr)
|
||||
}
|
||||
if len(removedAddrs) > 0 {
|
||||
removed = append(removed, peer.AddrInfo{ID: p.ID, Addrs: removedAddrs})
|
||||
}
|
||||
|
||||
if !found {
|
||||
keep = append(keep, peer)
|
||||
if len(keptAddrs) > 0 {
|
||||
keep = append(keep, peer.AddrInfo{ID: p.ID, Addrs: keptAddrs})
|
||||
}
|
||||
}
|
||||
cfg.SetBootstrapPeers(keep)
|
||||
@ -381,10 +402,10 @@ func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []config.Bootstra
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return removed, nil
|
||||
return config.BootstrapPeerStrings(removed), nil
|
||||
}
|
||||
|
||||
func bootstrapRemoveAll(r repo.Repo, cfg *config.Config) ([]config.BootstrapPeer, error) {
|
||||
func bootstrapRemoveAll(r repo.Repo, cfg *config.Config) ([]string, error) {
|
||||
removed, err := cfg.BootstrapPeers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -394,8 +415,7 @@ func bootstrapRemoveAll(r repo.Repo, cfg *config.Config) ([]config.BootstrapPeer
|
||||
if err := r.SetConfig(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return removed, nil
|
||||
return config.BootstrapPeerStrings(removed), nil
|
||||
}
|
||||
|
||||
const bootstrapSecurityWarning = `
|
||||
|
||||
@ -104,7 +104,7 @@ var CatCmd = &cmds.Command{
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Warningf("cat postrun: received unexpected type %T", val)
|
||||
log.Warnf("cat postrun: received unexpected type %T", val)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,6 +31,7 @@ var CidCmd = &cmds.Command{
|
||||
const (
|
||||
cidFormatOptionName = "f"
|
||||
cidVerisonOptionName = "v"
|
||||
cidCodecOptionName = "codec"
|
||||
cidMultibaseOptionName = "b"
|
||||
)
|
||||
|
||||
@ -49,11 +50,13 @@ The optional format string is a printf style format string:
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption(cidFormatOptionName, "Printf style format string.").WithDefault("%s"),
|
||||
cmds.StringOption(cidVerisonOptionName, "CID version to convert to."),
|
||||
cmds.StringOption(cidCodecOptionName, "CID codec to convert to."),
|
||||
cmds.StringOption(cidMultibaseOptionName, "Multibase to display CID in."),
|
||||
},
|
||||
Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
fmtStr, _ := req.Options[cidFormatOptionName].(string)
|
||||
verStr, _ := req.Options[cidVerisonOptionName].(string)
|
||||
codecStr, _ := req.Options[cidCodecOptionName].(string)
|
||||
baseStr, _ := req.Options[cidMultibaseOptionName].(string)
|
||||
|
||||
opts := cidFormatOpts{}
|
||||
@ -63,10 +66,21 @@ The optional format string is a printf style format string:
|
||||
}
|
||||
opts.fmtStr = fmtStr
|
||||
|
||||
if codecStr != "" {
|
||||
codec, ok := cid.Codecs[codecStr]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown IPLD codec: %s", codecStr)
|
||||
}
|
||||
opts.newCodec = codec
|
||||
} // otherwise, leave it as 0 (not a valid IPLD codec)
|
||||
|
||||
switch verStr {
|
||||
case "":
|
||||
// noop
|
||||
case "0":
|
||||
if opts.newCodec != 0 && opts.newCodec != cid.DagProtobuf {
|
||||
return fmt.Errorf("cannot convert to CIDv0 with any codec other than DagPB")
|
||||
}
|
||||
opts.verConv = toCidV0
|
||||
case "1":
|
||||
opts.verConv = toCidV1
|
||||
@ -101,7 +115,7 @@ The optional format string is a printf style format string:
|
||||
|
||||
type CidFormatRes struct {
|
||||
CidStr string // Original Cid String passed in
|
||||
Formatted string // Formated Result
|
||||
Formatted string // Formatted Result
|
||||
ErrorMsg string // Error
|
||||
}
|
||||
|
||||
@ -125,9 +139,10 @@ var base32Cmd = &cmds.Command{
|
||||
}
|
||||
|
||||
type cidFormatOpts struct {
|
||||
fmtStr string
|
||||
newBase mbase.Encoding
|
||||
verConv func(cid cid.Cid) (cid.Cid, error)
|
||||
fmtStr string
|
||||
newBase mbase.Encoding
|
||||
verConv func(cid cid.Cid) (cid.Cid, error)
|
||||
newCodec uint64
|
||||
}
|
||||
|
||||
type argumentIterator struct {
|
||||
@ -169,10 +184,11 @@ func emitCids(req *cmds.Request, resp cmds.ResponseEmitter, opts cidFormatOpts)
|
||||
emitErr = resp.Emit(res)
|
||||
continue
|
||||
}
|
||||
base := opts.newBase
|
||||
if base == -1 {
|
||||
base, _ = cid.ExtractEncoding(cidStr)
|
||||
|
||||
if opts.newCodec != 0 && opts.newCodec != c.Type() {
|
||||
c = cid.NewCidV1(opts.newCodec, c.Hash())
|
||||
}
|
||||
|
||||
if opts.verConv != nil {
|
||||
c, err = opts.verConv(c)
|
||||
if err != nil {
|
||||
@ -181,6 +197,16 @@ func emitCids(req *cmds.Request, resp cmds.ResponseEmitter, opts cidFormatOpts)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
base := opts.newBase
|
||||
if base == -1 {
|
||||
if c.Version() == 0 {
|
||||
base = mbase.Base58BTC
|
||||
} else {
|
||||
base, _ = cid.ExtractEncoding(cidStr)
|
||||
}
|
||||
}
|
||||
|
||||
str, err := cidutil.Format(opts.fmtStr, base, c)
|
||||
if _, ok := err.(cidutil.FormatStringError); ok {
|
||||
// no point in continuing if there is a problem with the format string
|
||||
@ -229,7 +255,7 @@ var basesCmd = &cmds.Command{
|
||||
Tagline: "List available multibase encodings.",
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(prefixOptionName, "also include the single leter prefixes in addition to the code"),
|
||||
cmds.BoolOption(prefixOptionName, "also include the single letter prefixes in addition to the code"),
|
||||
cmds.BoolOption(numericOptionName, "also include numeric codes"),
|
||||
},
|
||||
Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
|
||||
@ -26,7 +26,7 @@ func (e *commandEncoder) Encode(v interface{}) error {
|
||||
)
|
||||
|
||||
if cmd, ok = v.(*Command); !ok {
|
||||
return fmt.Errorf(`core/commands: uenxpected type %T, expected *"core/commands".Command`, v)
|
||||
return fmt.Errorf(`core/commands: unexpected type %T, expected *"core/commands".Command`, v)
|
||||
}
|
||||
|
||||
for _, s := range cmdPathStrings(cmd, cmd.showOpts) {
|
||||
|
||||
@ -95,7 +95,9 @@ func TestCommands(t *testing.T) {
|
||||
"/config/profile/apply",
|
||||
"/dag",
|
||||
"/dag/get",
|
||||
"/dag/export",
|
||||
"/dag/put",
|
||||
"/dag/import",
|
||||
"/dag/resolve",
|
||||
"/dht",
|
||||
"/dht/findpeer",
|
||||
|
||||
@ -1,29 +1,48 @@
|
||||
package dagcmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
"github.com/ipfs/go-ipfs/core/coredag"
|
||||
iface "github.com/ipfs/interface-go-ipfs-core"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cidenc "github.com/ipfs/go-cidutil/cidenc"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
mdag "github.com/ipfs/go-merkledag"
|
||||
ipfspath "github.com/ipfs/go-path"
|
||||
"github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
|
||||
gocar "github.com/ipld/go-car"
|
||||
//gipfree "github.com/ipld/go-ipld-prime/impl/free"
|
||||
//gipselector "github.com/ipld/go-ipld-prime/traversal/selector"
|
||||
//gipselectorbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||
|
||||
"gopkg.in/cheggaaa/pb.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
progressOptionName = "progress"
|
||||
silentOptionName = "silent"
|
||||
pinRootsOptionName = "pin-roots"
|
||||
)
|
||||
|
||||
var DagCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Interact with ipld dag objects.",
|
||||
ShortDescription: `
|
||||
'ipfs dag' is used for creating and manipulating dag objects.
|
||||
'ipfs dag' is used for creating and manipulating dag objects/hierarchies.
|
||||
|
||||
This subcommand is currently an experimental feature, but it is intended
|
||||
to deprecate and replace the existing 'ipfs object' command moving forward.
|
||||
@ -33,6 +52,8 @@ to deprecate and replace the existing 'ipfs object' command moving forward.
|
||||
"put": DagPutCmd,
|
||||
"get": DagGetCmd,
|
||||
"resolve": DagResolveCmd,
|
||||
"import": DagImportCmd,
|
||||
"export": DagExportCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@ -47,6 +68,15 @@ type ResolveOutput struct {
|
||||
RemPath string
|
||||
}
|
||||
|
||||
// CarImportOutput is the output type of the 'dag import' commands
|
||||
type CarImportOutput struct {
|
||||
Root RootMeta
|
||||
}
|
||||
type RootMeta struct {
|
||||
Cid cid.Cid
|
||||
PinErrorMsg string
|
||||
}
|
||||
|
||||
var DagPutCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Add a dag node to ipfs.",
|
||||
@ -187,7 +217,7 @@ var DagResolveCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Resolve ipld block",
|
||||
ShortDescription: `
|
||||
'ipfs dag resolve' fetches a dag node from ipfs, prints it's address and remaining path.
|
||||
'ipfs dag resolve' fetches a dag node from ipfs, prints its address and remaining path.
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
@ -241,3 +271,400 @@ var DagResolveCmd = &cmds.Command{
|
||||
},
|
||||
Type: ResolveOutput{},
|
||||
}
|
||||
|
||||
type importResult struct {
|
||||
roots map[cid.Cid]struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
var DagImportCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Import the contents of .car files",
|
||||
ShortDescription: `
|
||||
'ipfs dag import' imports all blocks present in supplied .car
|
||||
( Content Address aRchive ) files, recursively pinning any roots
|
||||
specified in the CAR file headers, unless --pin-roots is set to false.
|
||||
|
||||
Note:
|
||||
This command will import all blocks in the CAR file, not just those
|
||||
reachable from the specified roots. However, these other blocks will
|
||||
not be pinned and may be garbage collected later.
|
||||
|
||||
The pinning of the roots happens after all car files are processed,
|
||||
permitting import of DAGs spanning multiple files.
|
||||
|
||||
Pinning takes place in offline-mode exclusively, one root at a time.
|
||||
If the combination of blocks from the imported CAR files and what is
|
||||
currently present in the blockstore does not represent a complete DAG,
|
||||
pinning of that individual root will fail.
|
||||
|
||||
Maximum supported CAR version: 1
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.FileArg("path", true, true, "The path of a .car file.").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(silentOptionName, "No output."),
|
||||
cmds.BoolOption(pinRootsOptionName, "Pin optional roots listed in the .car headers after importing.").WithDefault(true),
|
||||
},
|
||||
Type: CarImportOutput{},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
|
||||
node, err := cmdenv.GetNode(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api, err := cmdenv.GetApi(env, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// on import ensure we do not reach out to the network for any reason
|
||||
// if a pin based on what is imported + what is in the blockstore
|
||||
// isn't possible: tough luck
|
||||
api, err = api.WithOptions(options.Api.Offline(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// grab a pinlock ( which doubles as a GC lock ) so that regardless of the
|
||||
// size of the streamed-in cars nothing will disappear on us before we had
|
||||
// a chance to roots that may show up at the very end
|
||||
// This is especially important for use cases like dagger:
|
||||
// ipfs dag import $( ... | ipfs-dagger --stdout=carfifos )
|
||||
//
|
||||
unlocker := node.Blockstore.PinLock()
|
||||
defer unlocker.Unlock()
|
||||
|
||||
doPinRoots, _ := req.Options[pinRootsOptionName].(bool)
|
||||
|
||||
retCh := make(chan importResult, 1)
|
||||
go importWorker(req, res, api, retCh)
|
||||
|
||||
done := <-retCh
|
||||
if done.err != nil {
|
||||
return done.err
|
||||
}
|
||||
|
||||
// It is not guaranteed that a root in a header is actually present in the same ( or any )
|
||||
// .car file. This is the case in version 1, and ideally in further versions too
|
||||
// Accumulate any root CID seen in a header, and supplement its actual node if/when encountered
|
||||
// We will attempt a pin *only* at the end in case all car files were well formed
|
||||
//
|
||||
// The boolean value indicates whether we have encountered the root within the car file's
|
||||
roots := done.roots
|
||||
|
||||
// opportunistic pinning: try whatever sticks
|
||||
if doPinRoots {
|
||||
|
||||
var failedPins int
|
||||
for c := range roots {
|
||||
|
||||
// We need to re-retrieve a block, convert it to ipld, and feed it
|
||||
// to the Pinning interface, sigh...
|
||||
//
|
||||
// If we didn't have the problem of inability to take multiple pinlocks,
|
||||
// we could use the api directly like so (though internally it does the same):
|
||||
//
|
||||
// // not ideal, but the pinning api takes only paths :(
|
||||
// rp := path.NewResolvedPath(
|
||||
// ipfspath.FromCid(c),
|
||||
// c,
|
||||
// c,
|
||||
// "",
|
||||
// )
|
||||
//
|
||||
// if err := api.Pin().Add(req.Context, rp, options.Pin.Recursive(true)); err != nil {
|
||||
|
||||
ret := RootMeta{Cid: c}
|
||||
|
||||
if block, err := node.Blockstore.Get(c); err != nil {
|
||||
ret.PinErrorMsg = err.Error()
|
||||
} else if nd, err := ipld.Decode(block); err != nil {
|
||||
ret.PinErrorMsg = err.Error()
|
||||
} else if err := node.Pinning.Pin(req.Context, nd, true); err != nil {
|
||||
ret.PinErrorMsg = err.Error()
|
||||
} else if err := node.Pinning.Flush(req.Context); err != nil {
|
||||
ret.PinErrorMsg = err.Error()
|
||||
}
|
||||
|
||||
if ret.PinErrorMsg != "" {
|
||||
failedPins++
|
||||
}
|
||||
|
||||
if err := res.Emit(&CarImportOutput{Root: ret}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if failedPins > 0 {
|
||||
return fmt.Errorf(
|
||||
"unable to pin all roots: %d out of %d failed",
|
||||
failedPins,
|
||||
len(roots),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, event *CarImportOutput) error {
|
||||
|
||||
silent, _ := req.Options[silentOptionName].(bool)
|
||||
if silent {
|
||||
return nil
|
||||
}
|
||||
|
||||
enc, err := cmdenv.GetLowLevelCidEncoder(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if event.Root.PinErrorMsg != "" {
|
||||
event.Root.PinErrorMsg = fmt.Sprintf("FAILED: %s", event.Root.PinErrorMsg)
|
||||
} else {
|
||||
event.Root.PinErrorMsg = "success"
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(
|
||||
w,
|
||||
"Pinned root\t%s\t%s\n",
|
||||
enc.Encode(event.Root.Cid),
|
||||
event.Root.PinErrorMsg,
|
||||
)
|
||||
return err
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
func importWorker(req *cmds.Request, re cmds.ResponseEmitter, api iface.CoreAPI, ret chan importResult) {
|
||||
|
||||
// this is *not* a transaction
|
||||
// it is simply a way to relieve pressure on the blockstore
|
||||
// similar to pinner.Pin/pinner.Flush
|
||||
batch := ipld.NewBatch(req.Context, api.Dag())
|
||||
|
||||
roots := make(map[cid.Cid]struct{})
|
||||
|
||||
it := req.Files.Entries()
|
||||
for it.Next() {
|
||||
|
||||
file := files.FileFromEntry(it)
|
||||
if file == nil {
|
||||
ret <- importResult{err: errors.New("expected a file handle")}
|
||||
return
|
||||
}
|
||||
|
||||
// wrap a defer-closer-scope
|
||||
//
|
||||
// every single file in it() is already open before we start
|
||||
// just close here sooner rather than later for neatness
|
||||
// and to surface potential errors writing on closed fifos
|
||||
// this won't/can't help with not running out of handles
|
||||
err := func() error {
|
||||
defer file.Close()
|
||||
|
||||
car, err := gocar.NewCarReader(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Be explicit here, until the spec is finished
|
||||
if car.Header.Version != 1 {
|
||||
return errors.New("only car files version 1 supported at present")
|
||||
}
|
||||
|
||||
for _, c := range car.Header.Roots {
|
||||
roots[c] = struct{}{}
|
||||
}
|
||||
|
||||
for {
|
||||
block, err := car.Next()
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
} else if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// the double-decode is suboptimal, but we need it for batching
|
||||
nd, err := ipld.Decode(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batch.Add(req.Context, nd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
ret <- importResult{err: err}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := it.Err(); err != nil {
|
||||
ret <- importResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
if err := batch.Commit(); err != nil {
|
||||
ret <- importResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
ret <- importResult{roots: roots}
|
||||
}
|
||||
|
||||
var DagExportCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Streams the selected DAG as a .car stream on stdout.",
|
||||
ShortDescription: `
|
||||
'ipfs dag export' fetches a dag and streams it out as a well-formed .car file.
|
||||
Note that at present only single root selections / .car files are supported.
|
||||
The output of blocks happens in strict DAG-traversal, first-seen, order.
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("root", true, false, "CID of a root to recursively export").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(progressOptionName, "p", "Display progress on CLI. Defaults to true when STDERR is a TTY."),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
|
||||
c, err := cid.Decode(req.Arguments[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"unable to parse root specification (currently only bare CIDs are supported): %s",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
node, err := cmdenv.GetNode(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Code disabled until descent-issue in go-ipld-prime is fixed
|
||||
// https://github.com/ribasushi/gip-muddle-up
|
||||
//
|
||||
// sb := gipselectorbuilder.NewSelectorSpecBuilder(gipfree.NodeBuilder())
|
||||
// car := gocar.NewSelectiveCar(
|
||||
// req.Context,
|
||||
// <needs to be fixed to take format.NodeGetter as well>,
|
||||
// []gocar.Dag{gocar.Dag{
|
||||
// Root: c,
|
||||
// Selector: sb.ExploreRecursive(
|
||||
// gipselector.RecursionLimitNone(),
|
||||
// sb.ExploreAll(sb.ExploreRecursiveEdge()),
|
||||
// ).Node(),
|
||||
// }},
|
||||
// )
|
||||
// ...
|
||||
// if err := car.Write(pipeW); err != nil {}
|
||||
|
||||
pipeR, pipeW := io.Pipe()
|
||||
|
||||
errCh := make(chan error, 2) // we only report the 1st error
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := pipeW.Close(); err != nil {
|
||||
errCh <- fmt.Errorf("stream flush failed: %s", err)
|
||||
}
|
||||
close(errCh)
|
||||
}()
|
||||
|
||||
if err := gocar.WriteCar(
|
||||
req.Context,
|
||||
mdag.NewSession(
|
||||
req.Context,
|
||||
node.DAG,
|
||||
),
|
||||
[]cid.Cid{c},
|
||||
pipeW,
|
||||
); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
if err := res.Emit(pipeR); err != nil {
|
||||
pipeR.Close() // ignore the error if any
|
||||
return err
|
||||
}
|
||||
|
||||
err = <-errCh
|
||||
|
||||
// minimal user friendliness
|
||||
if err != nil &&
|
||||
!node.IsOnline &&
|
||||
err == ipld.ErrNotFound {
|
||||
err = fmt.Errorf("%s (currently offline, perhaps retry after attaching to the network)", err)
|
||||
}
|
||||
|
||||
return err
|
||||
},
|
||||
PostRun: cmds.PostRunMap{
|
||||
cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
|
||||
|
||||
var showProgress bool
|
||||
val, specified := res.Request().Options[progressOptionName]
|
||||
if !specified {
|
||||
// default based on TTY availability
|
||||
errStat, _ := os.Stderr.Stat()
|
||||
if 0 != (errStat.Mode() & os.ModeCharDevice) {
|
||||
showProgress = true
|
||||
}
|
||||
} else if val.(bool) {
|
||||
showProgress = true
|
||||
}
|
||||
|
||||
// simple passthrough, no progress
|
||||
if !showProgress {
|
||||
return cmds.Copy(re, res)
|
||||
}
|
||||
|
||||
bar := pb.New64(0).SetUnits(pb.U_BYTES)
|
||||
bar.Output = os.Stderr
|
||||
bar.ShowSpeed = true
|
||||
bar.ShowElapsedTime = true
|
||||
bar.RefreshRate = 500 * time.Millisecond
|
||||
bar.Start()
|
||||
|
||||
var processedOneResponse bool
|
||||
for {
|
||||
v, err := res.Next()
|
||||
if err == io.EOF {
|
||||
|
||||
// We only write the final bar update on success
|
||||
// On error it looks too weird
|
||||
bar.Finish()
|
||||
|
||||
return re.Close()
|
||||
} else if err != nil {
|
||||
return re.CloseWithError(err)
|
||||
} else if processedOneResponse {
|
||||
return re.CloseWithError(errors.New("unexpected multipart response during emit, please file a bugreport"))
|
||||
}
|
||||
|
||||
r, ok := v.(io.Reader)
|
||||
if !ok {
|
||||
// some sort of encoded response, this should not be happening
|
||||
return errors.New("unexpected non-stream passed to PostRun: please file a bugreport")
|
||||
}
|
||||
|
||||
processedOneResponse = true
|
||||
|
||||
if err := re.Emit(bar.NewProxyReader(r)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
@ -15,10 +16,8 @@ import (
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
path "github.com/ipfs/go-path"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
notif "github.com/libp2p/go-libp2p-routing/notifications"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
b58 "github.com/mr-tron/base58/base58"
|
||||
)
|
||||
|
||||
@ -69,27 +68,36 @@ var queryDhtCmd = &cmds.Command{
|
||||
return ErrNotDHT
|
||||
}
|
||||
|
||||
id, err := peer.IDB58Decode(req.Arguments[0])
|
||||
id, err := peer.Decode(req.Arguments[0])
|
||||
if err != nil {
|
||||
return cmds.ClientError("invalid peer ID")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
closestPeers, err := nd.DHT.GetClosestPeers(ctx, string(id))
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
dht := nd.DHT.WAN
|
||||
if !nd.DHT.WANActive() {
|
||||
dht = nd.DHT.LAN
|
||||
}
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
defer cancel()
|
||||
for p := range closestPeers {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
ID: p,
|
||||
Type: notif.FinalPeer,
|
||||
})
|
||||
closestPeers, err := dht.GetClosestPeers(ctx, string(id))
|
||||
if closestPeers != nil {
|
||||
for p := range closestPeers {
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
ID: p,
|
||||
Type: routing.FinalPeer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
@ -99,15 +107,13 @@ var queryDhtCmd = &cmds.Command{
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return <-errCh
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.PeerResponse: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
for _, p := range obj.Responses {
|
||||
fmt.Fprintf(out, "%s\n", p.ID.Pretty())
|
||||
}
|
||||
routing.FinalPeer: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
fmt.Fprintf(out, "%s\n", obj.ID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -115,7 +121,7 @@ var queryDhtCmd = &cmds.Command{
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
const (
|
||||
@ -157,7 +163,7 @@ var findProvidersDhtCmd = &cmds.Command{
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
pchan := n.Routing.FindProvidersAsync(ctx, c, numProviders)
|
||||
|
||||
@ -165,9 +171,9 @@ var findProvidersDhtCmd = &cmds.Command{
|
||||
defer cancel()
|
||||
for p := range pchan {
|
||||
np := p
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.Provider,
|
||||
Responses: []*pstore.PeerInfo{&np},
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.Provider,
|
||||
Responses: []*peer.AddrInfo{&np},
|
||||
})
|
||||
}
|
||||
}()
|
||||
@ -180,15 +186,15 @@ var findProvidersDhtCmd = &cmds.Command{
|
||||
return nil
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.FinalPeer: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.FinalPeer: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "* closest peer %s\n", obj.ID)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
notif.Provider: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.Provider: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
prov := obj.Responses[0]
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "provider: ")
|
||||
@ -207,7 +213,7 @@ var findProvidersDhtCmd = &cmds.Command{
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
const (
|
||||
@ -269,7 +275,7 @@ var provideRefDhtCmd = &cmds.Command{
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
var provideErr error
|
||||
go func() {
|
||||
@ -280,8 +286,8 @@ var provideRefDhtCmd = &cmds.Command{
|
||||
provideErr = provideKeys(ctx, nd.Routing, cids)
|
||||
}
|
||||
if provideErr != nil {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.QueryError,
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.QueryError,
|
||||
Extra: provideErr.Error(),
|
||||
})
|
||||
}
|
||||
@ -296,9 +302,9 @@ var provideRefDhtCmd = &cmds.Command{
|
||||
return provideErr
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.FinalPeer: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.FinalPeer: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "sending provider record to peer %s\n", obj.ID)
|
||||
}
|
||||
@ -310,10 +316,10 @@ var provideRefDhtCmd = &cmds.Command{
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []cid.Cid) error {
|
||||
func provideKeys(ctx context.Context, r routing.Routing, cids []cid.Cid) error {
|
||||
for _, c := range cids {
|
||||
err := r.Provide(ctx, c, true)
|
||||
if err != nil {
|
||||
@ -323,12 +329,12 @@ func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []cid.Cid) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func provideKeysRec(ctx context.Context, r routing.IpfsRouting, dserv ipld.DAGService, cids []cid.Cid) error {
|
||||
func provideKeysRec(ctx context.Context, r routing.Routing, dserv ipld.DAGService, cids []cid.Cid) error {
|
||||
provided := cid.NewSet()
|
||||
for _, c := range cids {
|
||||
kset := cid.NewSet()
|
||||
|
||||
err := dag.EnumerateChildrenAsync(ctx, dag.GetLinksDirect(dserv), c, kset.Visit)
|
||||
err := dag.Walk(ctx, dag.GetLinksDirect(dserv), c, kset.Visit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -371,30 +377,30 @@ var findPeerDhtCmd = &cmds.Command{
|
||||
return ErrNotOnline
|
||||
}
|
||||
|
||||
pid, err := peer.IDB58Decode(req.Arguments[0])
|
||||
pid, err := peer.Decode(req.Arguments[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
var findPeerErr error
|
||||
go func() {
|
||||
defer cancel()
|
||||
var pi pstore.PeerInfo
|
||||
var pi peer.AddrInfo
|
||||
pi, findPeerErr = nd.Routing.FindPeer(ctx, pid)
|
||||
if findPeerErr != nil {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.QueryError,
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.QueryError,
|
||||
Extra: findPeerErr.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.FinalPeer,
|
||||
Responses: []*pstore.PeerInfo{&pi},
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.FinalPeer,
|
||||
Responses: []*peer.AddrInfo{&pi},
|
||||
})
|
||||
}()
|
||||
|
||||
@ -407,9 +413,9 @@ var findPeerDhtCmd = &cmds.Command{
|
||||
return findPeerErr
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.FinalPeer: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.FinalPeer: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
pi := obj.Responses[0]
|
||||
for _, a := range pi.Addrs {
|
||||
fmt.Fprintf(out, "%s\n", a)
|
||||
@ -422,7 +428,7 @@ var findPeerDhtCmd = &cmds.Command{
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
var getValueDhtCmd = &cmds.Command{
|
||||
@ -461,7 +467,7 @@ Different key types can specify other 'best' rules.
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
var getErr error
|
||||
go func() {
|
||||
@ -469,13 +475,13 @@ Different key types can specify other 'best' rules.
|
||||
var val []byte
|
||||
val, getErr = nd.Routing.GetValue(ctx, dhtkey)
|
||||
if getErr != nil {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.QueryError,
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.QueryError,
|
||||
Extra: getErr.Error(),
|
||||
})
|
||||
} else {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.Value,
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.Value,
|
||||
Extra: base64.StdEncoding.EncodeToString(val),
|
||||
})
|
||||
}
|
||||
@ -490,9 +496,9 @@ Different key types can specify other 'best' rules.
|
||||
return getErr
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.Value: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.Value: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
if verbose {
|
||||
_, err := fmt.Fprintf(out, "got value: '%s'\n", obj.Extra)
|
||||
return err
|
||||
@ -510,15 +516,15 @@ Different key types can specify other 'best' rules.
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
var putValueDhtCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Write a key/value pair to the routing system.",
|
||||
ShortDescription: `
|
||||
Given a key of the form /foo/bar and a value of any form, this will write that
|
||||
value to the routing system with that key.
|
||||
Given a key of the form /foo/bar and a valid value for that key, this will write
|
||||
that value to the routing system with that key.
|
||||
|
||||
Keys have two parts: a keytype (foo) and the key name (bar). IPNS uses the
|
||||
/ipns keytype, and expects the key name to be a Peer ID. IPNS entries are
|
||||
@ -529,15 +535,15 @@ this is only /ipns. Unless you have a relatively deep understanding of the
|
||||
go-ipfs routing internals, you likely want to be using 'ipfs name publish' instead
|
||||
of this.
|
||||
|
||||
Value is arbitrary text. Standard input can be used to provide value.
|
||||
|
||||
NOTE: A value may not exceed 2048 bytes.
|
||||
The value must be a valid value for the given key type. For example, if the key
|
||||
is /ipns/QmFoo, the value must be IPNS record (protobuf) signed with the key
|
||||
identified by QmFoo.
|
||||
`,
|
||||
},
|
||||
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("key", true, false, "The key to store the value at."),
|
||||
cmds.StringArg("value", true, false, "The value to store.").EnableStdin(),
|
||||
cmds.FileArg("value-file", true, false, "A path to a file containing the value to store.").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(dhtVerboseOptionName, "v", "Print extra information."),
|
||||
@ -548,12 +554,6 @@ NOTE: A value may not exceed 2048 bytes.
|
||||
return err
|
||||
}
|
||||
|
||||
// Needed to parse stdin args.
|
||||
err = req.ParseBodyArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !nd.IsOnline {
|
||||
return ErrNotOnline
|
||||
}
|
||||
@ -563,18 +563,27 @@ NOTE: A value may not exceed 2048 bytes.
|
||||
return err
|
||||
}
|
||||
|
||||
data := req.Arguments[1]
|
||||
file, err := cmdenv.GetFileArg(req.Files.Entries())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(req.Context)
|
||||
ctx, events := notif.RegisterForQueryEvents(ctx)
|
||||
ctx, events := routing.RegisterForQueryEvents(ctx)
|
||||
|
||||
var putErr error
|
||||
go func() {
|
||||
defer cancel()
|
||||
putErr = nd.Routing.PutValue(ctx, key, []byte(data))
|
||||
if putErr != nil {
|
||||
notif.PublishQueryEvent(ctx, ¬if.QueryEvent{
|
||||
Type: notif.QueryError,
|
||||
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
|
||||
Type: routing.QueryError,
|
||||
Extra: putErr.Error(),
|
||||
})
|
||||
}
|
||||
@ -589,15 +598,15 @@ NOTE: A value may not exceed 2048 bytes.
|
||||
return putErr
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *notif.QueryEvent) error {
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error {
|
||||
pfm := pfuncMap{
|
||||
notif.FinalPeer: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.FinalPeer: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "* closest peer %s\n", obj.ID)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
notif.Value: func(obj *notif.QueryEvent, out io.Writer, verbose bool) error {
|
||||
routing.Value: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error {
|
||||
fmt.Fprintf(out, "%s\n", obj.ID.Pretty())
|
||||
return nil
|
||||
},
|
||||
@ -608,13 +617,13 @@ NOTE: A value may not exceed 2048 bytes.
|
||||
return printEvent(out, w, verbose, pfm)
|
||||
}),
|
||||
},
|
||||
Type: notif.QueryEvent{},
|
||||
Type: routing.QueryEvent{},
|
||||
}
|
||||
|
||||
type printFunc func(obj *notif.QueryEvent, out io.Writer, verbose bool) error
|
||||
type pfuncMap map[notif.QueryEventType]printFunc
|
||||
type printFunc func(obj *routing.QueryEvent, out io.Writer, verbose bool) error
|
||||
type pfuncMap map[routing.QueryEventType]printFunc
|
||||
|
||||
func printEvent(obj *notif.QueryEvent, out io.Writer, verbose bool, override pfuncMap) error {
|
||||
func printEvent(obj *routing.QueryEvent, out io.Writer, verbose bool, override pfuncMap) error {
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "%s: ", time.Now().Format("15:04:05.000"))
|
||||
}
|
||||
@ -626,17 +635,17 @@ func printEvent(obj *notif.QueryEvent, out io.Writer, verbose bool, override pfu
|
||||
}
|
||||
|
||||
switch obj.Type {
|
||||
case notif.SendingQuery:
|
||||
case routing.SendingQuery:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "* querying %s\n", obj.ID)
|
||||
}
|
||||
case notif.Value:
|
||||
case routing.Value:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "got value: '%s'\n", obj.Extra)
|
||||
} else {
|
||||
fmt.Fprint(out, obj.Extra)
|
||||
}
|
||||
case notif.PeerResponse:
|
||||
case routing.PeerResponse:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "* %s says use ", obj.ID)
|
||||
for _, p := range obj.Responses {
|
||||
@ -644,19 +653,19 @@ func printEvent(obj *notif.QueryEvent, out io.Writer, verbose bool, override pfu
|
||||
}
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
case notif.QueryError:
|
||||
case routing.QueryError:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "error: %s\n", obj.Extra)
|
||||
}
|
||||
case notif.DialingPeer:
|
||||
case routing.DialingPeer:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "dialing peer: %s\n", obj.ID)
|
||||
}
|
||||
case notif.AddingPeer:
|
||||
case routing.AddingPeer:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "adding peer to query: %s\n", obj.ID)
|
||||
}
|
||||
case notif.FinalPeer:
|
||||
case routing.FinalPeer:
|
||||
default:
|
||||
if verbose {
|
||||
fmt.Fprintf(out, "unrecognized event type: %d\n", obj.Type)
|
||||
|
||||
@ -6,11 +6,11 @@ import (
|
||||
"github.com/ipfs/go-ipfs/namesys"
|
||||
|
||||
ipns "github.com/ipfs/go-ipns"
|
||||
tu "github.com/libp2p/go-testutil"
|
||||
"github.com/libp2p/go-libp2p-core/test"
|
||||
)
|
||||
|
||||
func TestKeyTranslation(t *testing.T) {
|
||||
pid := tu.RandPeerIDFatal(t)
|
||||
pid := test.RandPeerIDFatal(t)
|
||||
pkname := namesys.PkKeyForID(pid)
|
||||
ipnsname := ipns.RecordKey(pid)
|
||||
|
||||
|
||||
@ -64,12 +64,12 @@ The resolver can recursively resolve:
|
||||
name := req.Arguments[0]
|
||||
resolver := namesys.NewDNSResolver()
|
||||
|
||||
var ropts []nsopts.ResolveOpt
|
||||
var routing []nsopts.ResolveOpt
|
||||
if !recursive {
|
||||
ropts = append(ropts, nsopts.Depth(1))
|
||||
routing = append(routing, nsopts.Depth(1))
|
||||
}
|
||||
|
||||
output, err := resolver.Resolve(req.Context, name, ropts...)
|
||||
output, err := resolver.Resolve(req.Context, name, routing...)
|
||||
if err != nil && (recursive || err != namesys.ErrResolveRecursion) {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
)
|
||||
|
||||
func ExternalBinary() *cmds.Command {
|
||||
func ExternalBinary(instructions string) *cmds.Command {
|
||||
return &cmds.Command{
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("args", false, true, "Arguments for subcommand."),
|
||||
@ -27,7 +27,7 @@ func ExternalBinary() *cmds.Command {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprintf(buf, "%s is an 'external' command.\n", binname)
|
||||
fmt.Fprintf(buf, "It does not currently appear to be installed.\n")
|
||||
fmt.Fprintf(buf, "Please refer to the ipfs documentation for instructions.\n")
|
||||
fmt.Fprintf(buf, "%s\n", instructions)
|
||||
return res.Emit(buf)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,14 +17,14 @@ import (
|
||||
bservice "github.com/ipfs/go-blockservice"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cidenc "github.com/ipfs/go-cidutil/cidenc"
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-ipfs-exchange-offline"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
"github.com/ipfs/go-mfs"
|
||||
ft "github.com/ipfs/go-unixfs"
|
||||
"github.com/ipfs/interface-go-ipfs-core"
|
||||
iface "github.com/ipfs/interface-go-ipfs-core"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
@ -36,16 +36,32 @@ var FilesCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Interact with unixfs files.",
|
||||
ShortDescription: `
|
||||
Files is an API for manipulating IPFS objects as if they were a unix
|
||||
Files is an API for manipulating IPFS objects as if they were a Unix
|
||||
filesystem.
|
||||
|
||||
The files facility interacts with MFS (Mutable File System). MFS acts as a
|
||||
single, dynamic filesystem mount. MFS has a root CID that is transparently
|
||||
updated when a change happens (and can be checked with "ipfs files stat /").
|
||||
|
||||
All files and folders within MFS are respected and will not be cleaned up
|
||||
during garbage collections. MFS is independent from the list of pinned items
|
||||
("ipfs pin ls"). Calls to "ipfs pin add" and "ipfs pin rm" will add and remove
|
||||
pins independently of MFS. If MFS content that was
|
||||
additionally pinned is removed by calling "ipfs files rm", it will still
|
||||
remain pinned.
|
||||
|
||||
Content added with "ipfs add" (which by default also becomes pinned), is not
|
||||
added to MFS. Any content can be put into MFS with the command "ipfs files cp
|
||||
/ipfs/<cid> /some/path/".
|
||||
|
||||
|
||||
NOTE:
|
||||
Most of the subcommands of 'ipfs files' accept the '--flush' flag. It defaults
|
||||
to true. Use caution when setting this flag to false. It will improve
|
||||
performance for large numbers of file operations, but it does so at the cost
|
||||
of consistency guarantees. If the daemon is unexpectedly killed before running
|
||||
'ipfs files flush' on the files in question, then data may be lost. This also
|
||||
applies to running 'ipfs repo gc' concurrently with '--flush=false'
|
||||
applies to run 'ipfs repo gc' concurrently with '--flush=false'
|
||||
operations.
|
||||
`,
|
||||
},
|
||||
@ -305,11 +321,27 @@ func walkBlock(ctx context.Context, dagserv ipld.DAGService, nd ipld.Node) (bool
|
||||
|
||||
var filesCpCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Copy files into mfs.",
|
||||
Tagline: "Copy any IPFS files and directories into MFS (or copy within MFS).",
|
||||
ShortDescription: `
|
||||
"ipfs files cp" can be used to copy any IPFS file or directory (usually in the
|
||||
form /ipfs/<CID>, but also any resolvable path), into the Mutable File System
|
||||
(MFS).
|
||||
|
||||
It can also be used to copy files within MFS, but in the case when an
|
||||
IPFS-path matches an existing MFS path, the IPFS path wins.
|
||||
|
||||
In order to add content to MFS from disk, you can use "ipfs add" to obtain the
|
||||
IPFS Content Identifier and then "ipfs files cp" to copy it into MFS:
|
||||
|
||||
$ ipfs add --quieter --pin=false <your file>
|
||||
# ...
|
||||
# ... outputs the root CID at the end
|
||||
$ ipfs cp /ipfs/<CID> /your/desired/mfs/path
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("source", true, false, "Source object to copy."),
|
||||
cmds.StringArg("dest", true, false, "Destination to copy object to."),
|
||||
cmds.StringArg("source", true, false, "Source IPFS or MFS path to copy."),
|
||||
cmds.StringArg("dest", true, false, "Destination within MFS."),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
nd, err := cmdenv.GetNode(env)
|
||||
@ -379,7 +411,7 @@ type filesLsOutput struct {
|
||||
}
|
||||
|
||||
const (
|
||||
longOptionName = "l"
|
||||
longOptionName = "long"
|
||||
dontSortOptionName = "U"
|
||||
)
|
||||
|
||||
@ -408,7 +440,7 @@ Examples:
|
||||
cmds.StringArg("path", false, false, "Path to show listing for. Defaults to '/'."),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(longOptionName, "Use long listing format."),
|
||||
cmds.BoolOption(longOptionName, "l", "Use long listing format."),
|
||||
cmds.BoolOption(dontSortOptionName, "Do not sort; list entries in directory order."),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
@ -520,10 +552,10 @@ const (
|
||||
|
||||
var filesReadCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Read a file in a given mfs.",
|
||||
Tagline: "Read a file in a given MFS.",
|
||||
ShortDescription: `
|
||||
Read a specified number of bytes from a file at a given offset. By default,
|
||||
will read the entire file similar to unix cat.
|
||||
it will read the entire file similar to the Unix cat.
|
||||
|
||||
Examples:
|
||||
|
||||
@ -615,7 +647,7 @@ var filesMvCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Move files.",
|
||||
ShortDescription: `
|
||||
Move files around. Just like traditional unix mv.
|
||||
Move files around. Just like the traditional Unix mv.
|
||||
|
||||
Example:
|
||||
|
||||
@ -670,22 +702,23 @@ a beginning offset to write to. The entire length of the input will be
|
||||
written.
|
||||
|
||||
If the '--create' option is specified, the file will be created if it does not
|
||||
exist. Nonexistant intermediate directories will not be created.
|
||||
exist. Nonexistent intermediate directories will not be created unless the
|
||||
'--parents' option is specified.
|
||||
|
||||
Newly created files will have the same CID version and hash function of the
|
||||
parent directory unless the --cid-version and --hash options are used.
|
||||
parent directory unless the '--cid-version' and '--hash' options are used.
|
||||
|
||||
Newly created leaves will be in the legacy format (Protobuf) if the
|
||||
CID version is 0, or raw is the CID version is non-zero. Use of the
|
||||
--raw-leaves option will override this behavior.
|
||||
CID version is 0, or raw if the CID version is non-zero. Use of the
|
||||
'--raw-leaves' option will override this behavior.
|
||||
|
||||
If the '--flush' option is set to false, changes will not be propogated to the
|
||||
If the '--flush' option is set to false, changes will not be propagated to the
|
||||
merkledag root. This can make operations much faster when doing a large number
|
||||
of writes to a deeper directory structure.
|
||||
|
||||
EXAMPLE:
|
||||
|
||||
echo "hello world" | ipfs files write --create /myfs/a/b/file
|
||||
echo "hello world" | ipfs files write --create --parents /myfs/a/b/file
|
||||
echo "hello world" | ipfs files write --truncate /myfs/a/b/file
|
||||
|
||||
WARNING:
|
||||
@ -762,7 +795,7 @@ stat' on the file or any of its ancestors.
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
} else {
|
||||
log.Error("files: error closing file mfs file descriptor", err)
|
||||
flog.Error("files: error closing file mfs file descriptor", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -862,7 +895,7 @@ var filesFlushCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Flush a given path's data to disk.",
|
||||
ShortDescription: `
|
||||
Flush a given path to disk. This is only useful when other commands
|
||||
Flush a given path to the disk. This is only useful when other commands
|
||||
are run with the '--flush=false'.
|
||||
`,
|
||||
},
|
||||
@ -997,26 +1030,28 @@ Remove files or directories.
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
|
||||
dir, name := gopath.Split(path)
|
||||
parent, err := mfs.Lookup(nd.FilesRoot, dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parent lookup: %s", err)
|
||||
}
|
||||
|
||||
pdir, ok := parent.(*mfs.Directory)
|
||||
if !ok {
|
||||
return fmt.Errorf("no such file or directory: %s", path)
|
||||
}
|
||||
|
||||
// if '--force' specified, it will remove anything else,
|
||||
// including file, directory, corrupted node, etc
|
||||
force, _ := req.Options[forceOptionName].(bool)
|
||||
|
||||
dir, name := gopath.Split(path)
|
||||
|
||||
pdir, err := getParentDir(nd.FilesRoot, dir)
|
||||
if err != nil {
|
||||
if force && err == os.ErrNotExist {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("parent lookup: %s", err)
|
||||
}
|
||||
|
||||
if force {
|
||||
err := pdir.Unlink(name)
|
||||
if err != nil {
|
||||
if err == os.ErrNotExist {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return pdir.Flush()
|
||||
}
|
||||
|
||||
@ -1131,17 +1166,13 @@ func getFileHandle(r *mfs.Root, path string, create bool, builder cid.Builder) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if create is specified and the file doesnt exist, we create the file
|
||||
// if create is specified and the file doesn't exist, we create the file
|
||||
dirname, fname := gopath.Split(path)
|
||||
pdiri, err := mfs.Lookup(r, dirname)
|
||||
pdir, err := getParentDir(r, dirname)
|
||||
if err != nil {
|
||||
flog.Error("lookupfail ", dirname)
|
||||
return nil, err
|
||||
}
|
||||
pdir, ok := pdiri.(*mfs.Directory)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s was not a directory", dirname)
|
||||
}
|
||||
|
||||
if builder == nil {
|
||||
builder = pdir.GetCidBuilder()
|
||||
}
|
||||
@ -1160,7 +1191,7 @@ func getFileHandle(r *mfs.Root, path string, create bool, builder cid.Builder) (
|
||||
|
||||
fi, ok := fsn.(*mfs.File)
|
||||
if !ok {
|
||||
return nil, errors.New("expected *mfs.File, didnt get it. This is likely a race condition")
|
||||
return nil, errors.New("expected *mfs.File, didn't get it. This is likely a race condition")
|
||||
}
|
||||
return fi, nil
|
||||
|
||||
@ -1184,3 +1215,16 @@ func checkPath(p string) (string, error) {
|
||||
}
|
||||
return cleaned, nil
|
||||
}
|
||||
|
||||
func getParentDir(root *mfs.Root, dir string) (*mfs.Directory, error) {
|
||||
parent, err := mfs.Lookup(root, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pdir, ok := parent.(*mfs.Directory)
|
||||
if !ok {
|
||||
return nil, errors.New("expected *mfs.Directory, didn't get it. This is likely a race condition")
|
||||
}
|
||||
return pdir, nil
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
filestore "github.com/ipfs/go-filestore"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
filestore "github.com/ipfs/go-ipfs/filestore"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
|
||||
@ -155,7 +155,7 @@ func makeProgressBar(out io.Writer, l int64) *pb.ProgressBar {
|
||||
bar.Output = out
|
||||
|
||||
// the progress bar lib doesn't give us a way to get the width of the output,
|
||||
// so as a hack we just use a callback to measure the output, then git rid of it
|
||||
// so as a hack we just use a callback to measure the output, then get rid of it
|
||||
bar.Callback = func(line string) {
|
||||
terminalWidth := len(line)
|
||||
bar.Callback = nil
|
||||
|
||||
@ -8,14 +8,16 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
version "github.com/ipfs/go-ipfs"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
ic "github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
kb "github.com/libp2p/go-libp2p-kbucket"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
identify "github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
||||
)
|
||||
|
||||
@ -74,7 +76,7 @@ EXAMPLE:
|
||||
var id peer.ID
|
||||
if len(req.Arguments) > 0 {
|
||||
var err error
|
||||
id, err = peer.IDB58Decode(req.Arguments[0])
|
||||
id, err = peer.Decode(req.Arguments[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid peer id")
|
||||
}
|
||||
@ -183,12 +185,15 @@ func printSelf(node *core.IpfsNode) (interface{}, error) {
|
||||
info.PublicKey = base64.StdEncoding.EncodeToString(pkb)
|
||||
|
||||
if node.PeerHost != nil {
|
||||
for _, a := range node.PeerHost.Addrs() {
|
||||
s := a.String() + "/ipfs/" + info.ID
|
||||
info.Addresses = append(info.Addresses, s)
|
||||
addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(node.PeerHost))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range addrs {
|
||||
info.Addresses = append(info.Addresses, a.String())
|
||||
}
|
||||
}
|
||||
info.ProtocolVersion = identify.LibP2PVersion
|
||||
info.AgentVersion = identify.ClientVersion
|
||||
info.AgentVersion = version.UserAgent
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ var keyGenCmd = &cmds.Command{
|
||||
Tagline: "Create a new keypair",
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create [rsa, ed25519]"),
|
||||
cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create: rsa, ed25519").WithDefault("rsa"),
|
||||
cmds.IntOption(keyStoreSizeOptionName, "s", "size of the key to generate"),
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
|
||||
@ -24,8 +24,10 @@ output of a running daemon.
|
||||
|
||||
There are also two environmental variables that direct the logging
|
||||
system (not just for the daemon logs, but all commands):
|
||||
IPFS_LOGGING - sets the level of verbosity of the logging. One of: debug, info, warning, error, critical
|
||||
IPFS_LOGGING_FMT - sets formatting of the log output. One of: color, nocolor
|
||||
IPFS_LOGGING - sets the level of verbosity of the logging.
|
||||
One of: debug, info, warn, error, dpanic, panic, fatal
|
||||
IPFS_LOGGING_FMT - sets formatting of the log output.
|
||||
One of: color, nocolor
|
||||
`,
|
||||
},
|
||||
|
||||
@ -49,8 +51,8 @@ the event log.
|
||||
// TODO use a different keyword for 'all' because all can theoretically
|
||||
// clash with a subsystem name
|
||||
cmds.StringArg("subsystem", true, false, fmt.Sprintf("The subsystem logging identifier. Use '%s' for all subsystems.", logAllKeyword)),
|
||||
cmds.StringArg("level", true, false, `The log level, with 'debug' the most verbose and 'critical' the least verbose.
|
||||
One of: debug, info, warning, error, critical.
|
||||
cmds.StringArg("level", true, false, `The log level, with 'debug' the most verbose and 'fatal' the least verbose.
|
||||
One of: debug, info, warn, error, dpanic, panic, fatal.
|
||||
`),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
|
||||
@ -65,7 +65,7 @@ The JSON output contains type information.
|
||||
cmds.BoolOption(lsHeadersOptionNameTime, "v", "Print table headers (Hash, Size, Name)."),
|
||||
cmds.BoolOption(lsResolveTypeOptionName, "Resolve linked objects to find out their types.").WithDefault(true),
|
||||
cmds.BoolOption(lsSizeOptionName, "Resolve linked objects to find out their file size.").WithDefault(true),
|
||||
cmds.BoolOption(lsStreamOptionName, "s", "Enable exprimental streaming of directory entries as they are traversed."),
|
||||
cmds.BoolOption(lsStreamOptionName, "s", "Enable experimental streaming of directory entries as they are traversed."),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
api, err := cmdenv.GetApi(env, req)
|
||||
|
||||
@ -35,7 +35,7 @@ You may have to create /ipfs and /ipns before using 'ipfs mount':
|
||||
`,
|
||||
LongDescription: `
|
||||
Mount IPFS at a read-only mountpoint on the OS. The default, /ipfs and /ipns,
|
||||
are set in the configuration file, but can be overriden by the options.
|
||||
are set in the configuration file, but can be overridden by the options.
|
||||
All IPFS objects will be accessible under this directory. Note that the
|
||||
root will not be listable, as it is virtual. Access known paths directly.
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-record"
|
||||
)
|
||||
|
||||
@ -93,7 +93,7 @@ var ipnspsSubsCmd = &cmds.Command{
|
||||
log.Errorf("ipns key not a valid peer ID: %s", err)
|
||||
continue
|
||||
}
|
||||
paths = append(paths, "/ipns/"+peer.IDB58Encode(pid))
|
||||
paths = append(paths, "/ipns/"+peer.Encode(pid))
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &stringList{paths})
|
||||
@ -120,7 +120,7 @@ var ipnspsCancelCmd = &cmds.Command{
|
||||
|
||||
name := req.Arguments[0]
|
||||
name = strings.TrimPrefix(name, "/ipns/")
|
||||
pid, err := peer.IDB58Decode(name)
|
||||
pid, err := peer.Decode(name)
|
||||
if err != nil {
|
||||
return cmds.Errorf(cmds.ErrClient, err.Error())
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
"github.com/ipfs/go-ipfs/dagutils"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/ipfs/go-merkledag/dagutils"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -70,7 +70,7 @@ Example:
|
||||
out := make([]*dagutils.Change, len(changes))
|
||||
for i, change := range changes {
|
||||
out[i] = &dagutils.Change{
|
||||
Type: change.Type,
|
||||
Type: dagutils.ChangeType(change.Type),
|
||||
Path: change.Path,
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ type Object struct {
|
||||
Links []Link `json:"Links,omitempty"`
|
||||
}
|
||||
|
||||
var ErrDataEncoding = errors.New("unkown data field encoding")
|
||||
var ErrDataEncoding = errors.New("unknown data field encoding")
|
||||
|
||||
const (
|
||||
headersOptionName = "headers"
|
||||
@ -197,7 +197,7 @@ This command outputs data in the following encodings:
|
||||
* "xml"
|
||||
(Specified by the "--encoding" or "--enc" flag)
|
||||
|
||||
The encoding of the object's data field can be specifed by using the
|
||||
The encoding of the object's data field can be specified by using the
|
||||
--data-encoding flag
|
||||
|
||||
Supported values are:
|
||||
|
||||
@ -14,10 +14,10 @@ import (
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
p2p "github.com/ipfs/go-ipfs/p2p"
|
||||
|
||||
ipfsaddr "github.com/ipfs/go-ipfs-addr"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
madns "github.com/multiformats/go-multiaddr-dns"
|
||||
)
|
||||
@ -87,8 +87,8 @@ Forward connections made to <listen-address> to <target-address>.
|
||||
connections and/or handlers. It must be prefixed with '` + P2PProtoPrefix + `'.
|
||||
|
||||
Example:
|
||||
ipfs p2p forward ` + P2PProtoPrefix + `myproto /ip4/127.0.0.1/tcp/4567 /ipfs/QmPeer
|
||||
- Forward connections to 127.0.0.1:4567 to '` + P2PProtoPrefix + `myproto' service on /ipfs/QmPeer
|
||||
ipfs p2p forward ` + P2PProtoPrefix + `myproto /ip4/127.0.0.1/tcp/4567 /p2p/QmPeer
|
||||
- Forward connections to 127.0.0.1:4567 to '` + P2PProtoPrefix + `myproto' service on /p2p/QmPeer
|
||||
|
||||
`,
|
||||
},
|
||||
@ -133,37 +133,49 @@ Example:
|
||||
}
|
||||
|
||||
// parseIpfsAddr is a function that takes in addr string and return ipfsAddrs
|
||||
func parseIpfsAddr(addr string) ([]ipfsaddr.IPFSAddr, error) {
|
||||
mutiladdr, err := ma.NewMultiaddr(addr)
|
||||
func parseIpfsAddr(addr string) (*peer.AddrInfo, error) {
|
||||
multiaddr, err := ma.NewMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := mutiladdr.ValueForProtocol(ma.P_IPFS); err == nil {
|
||||
iaddrs := make([]ipfsaddr.IPFSAddr, 1)
|
||||
iaddrs[0], err = ipfsaddr.ParseMultiaddr(mutiladdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iaddrs, nil
|
||||
|
||||
pi, err := peer.AddrInfoFromP2pAddr(multiaddr)
|
||||
if err == nil {
|
||||
return pi, nil
|
||||
}
|
||||
// resolve mutiladdr whose protocol is not ma.P_IPFS
|
||||
|
||||
// resolve multiaddr whose protocol is not ma.P_IPFS
|
||||
ctx, cancel := context.WithTimeout(context.Background(), resolveTimeout)
|
||||
defer cancel()
|
||||
addrs, err := madns.Resolve(ctx, mutiladdr)
|
||||
addrs, err := madns.Resolve(ctx, multiaddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, errors.New("fail to resolve the multiaddr:" + mutiladdr.String())
|
||||
return nil, errors.New("fail to resolve the multiaddr:" + multiaddr.String())
|
||||
}
|
||||
iaddrs := make([]ipfsaddr.IPFSAddr, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
iaddrs[i], err = ipfsaddr.ParseMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var info peer.AddrInfo
|
||||
for _, addr := range addrs {
|
||||
taddr, id := peer.SplitAddr(addr)
|
||||
if id == "" {
|
||||
// not an ipfs addr, skipping.
|
||||
continue
|
||||
}
|
||||
switch info.ID {
|
||||
case "":
|
||||
info.ID = id
|
||||
case id:
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"ambiguous multiaddr %s could refer to %s or %s",
|
||||
multiaddr,
|
||||
info.ID,
|
||||
id,
|
||||
)
|
||||
}
|
||||
info.Addrs = append(info.Addrs, taddr)
|
||||
}
|
||||
return iaddrs, nil
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
var p2pListenCmd = &cmds.Command{
|
||||
@ -256,14 +268,10 @@ func checkPort(target ma.Multiaddr) error {
|
||||
}
|
||||
|
||||
// forwardLocal forwards local connections to a libp2p service
|
||||
func forwardLocal(ctx context.Context, p *p2p.P2P, ps pstore.Peerstore, proto protocol.ID, bindAddr ma.Multiaddr, addrs []ipfsaddr.IPFSAddr) error {
|
||||
for _, addr := range addrs {
|
||||
ps.AddAddr(addr.ID(), addr.Multiaddr(), pstore.TempAddrTTL)
|
||||
}
|
||||
func forwardLocal(ctx context.Context, p *p2p.P2P, ps pstore.Peerstore, proto protocol.ID, bindAddr ma.Multiaddr, addr *peer.AddrInfo) error {
|
||||
ps.AddAddrs(addr.ID, addr.Addrs, pstore.TempAddrTTL)
|
||||
// TODO: return some info
|
||||
// the length of the addrs must large than 0
|
||||
// peerIDs in addr must be the same and choose addr[0] to connect
|
||||
_, err := p.ForwardLocal(ctx, addrs[0].ID(), proto, bindAddr)
|
||||
_, err := p.ForwardLocal(ctx, addr.ID, proto, bindAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -2,26 +2,29 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cidenc "github.com/ipfs/go-cidutil/cidenc"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
pin "github.com/ipfs/go-ipfs-pinner"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
verifcid "github.com/ipfs/go-verifcid"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
options "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
"github.com/ipfs/interface-go-ipfs-core/path"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
||||
)
|
||||
|
||||
var PinCmd = &cmds.Command{
|
||||
@ -259,8 +262,9 @@ collected if needed. (By default, recursively. Use -r=false for direct pins.)
|
||||
}
|
||||
|
||||
const (
|
||||
pinTypeOptionName = "type"
|
||||
pinQuietOptionName = "quiet"
|
||||
pinTypeOptionName = "type"
|
||||
pinQuietOptionName = "quiet"
|
||||
pinStreamOptionName = "stream"
|
||||
)
|
||||
|
||||
var listPinCmd = &cmds.Command{
|
||||
@ -313,6 +317,7 @@ Example:
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption(pinTypeOptionName, "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\".").WithDefault("all"),
|
||||
cmds.BoolOption(pinQuietOptionName, "q", "Write just hashes of objects."),
|
||||
cmds.BoolOption(pinStreamOptionName, "s", "Enable streaming of pins as they are discovered."),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
n, err := cmdenv.GetNode(env)
|
||||
@ -326,9 +331,7 @@ Example:
|
||||
}
|
||||
|
||||
typeStr, _ := req.Options[pinTypeOptionName].(string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream, _ := req.Options[pinStreamOptionName].(bool)
|
||||
|
||||
switch typeStr {
|
||||
case "all", "direct", "indirect", "recursive":
|
||||
@ -337,34 +340,61 @@ Example:
|
||||
return err
|
||||
}
|
||||
|
||||
enc, err := cmdenv.GetCidEncoder(req)
|
||||
if err != nil {
|
||||
return err
|
||||
// For backward compatibility, we accumulate the pins in the same output type as before.
|
||||
emit := res.Emit
|
||||
lgcList := map[string]PinLsType{}
|
||||
if !stream {
|
||||
emit = func(v interface{}) error {
|
||||
obj := v.(*PinLsOutputWrapper)
|
||||
lgcList[obj.PinLsObject.Cid] = PinLsType{Type: obj.PinLsObject.Type}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var keys map[cid.Cid]RefKeyObject
|
||||
if len(req.Arguments) > 0 {
|
||||
keys, err = pinLsKeys(req.Context, req.Arguments, typeStr, n, api)
|
||||
err = pinLsKeys(req, typeStr, n, api, emit)
|
||||
} else {
|
||||
keys, err = pinLsAll(req.Context, typeStr, n)
|
||||
err = pinLsAll(req, typeStr, n.Pinning, n.DAG, emit)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refKeys := make(map[string]RefKeyObject, len(keys))
|
||||
for k, v := range keys {
|
||||
refKeys[enc.Encode(k)] = v
|
||||
if !stream {
|
||||
return cmds.EmitOnce(res, &PinLsOutputWrapper{
|
||||
PinLsList: PinLsList{Keys: lgcList},
|
||||
})
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &RefKeyList{Keys: refKeys})
|
||||
return nil
|
||||
},
|
||||
Type: RefKeyList{},
|
||||
Type: &PinLsOutputWrapper{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RefKeyList) error {
|
||||
quiet, _ := req.Options[pinQuietOptionName].(bool)
|
||||
cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PinLsOutputWrapper) error {
|
||||
stream, _ := req.Options[pinStreamOptionName].(bool)
|
||||
|
||||
for k, v := range out.Keys {
|
||||
enc := json.NewEncoder(w)
|
||||
|
||||
if stream {
|
||||
return enc.Encode(out.PinLsObject)
|
||||
}
|
||||
|
||||
return enc.Encode(out.PinLsList)
|
||||
}),
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PinLsOutputWrapper) error {
|
||||
quiet, _ := req.Options[pinQuietOptionName].(bool)
|
||||
stream, _ := req.Options[pinStreamOptionName].(bool)
|
||||
|
||||
if stream {
|
||||
if quiet {
|
||||
fmt.Fprintf(w, "%s\n", out.PinLsObject.Cid)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s %s\n", out.PinLsObject.Cid, out.PinLsObject.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range out.PinLsList.Keys {
|
||||
if quiet {
|
||||
fmt.Fprintf(w, "%s\n", k)
|
||||
} else {
|
||||
@ -377,6 +407,110 @@ Example:
|
||||
},
|
||||
}
|
||||
|
||||
// PinLsOutputWrapper is the output type of the pin ls command.
|
||||
// Pin ls needs to output two different type depending on if it's streamed or not.
|
||||
// We use this to bypass the cmds lib refusing to have interface{}
|
||||
type PinLsOutputWrapper struct {
|
||||
PinLsList
|
||||
PinLsObject
|
||||
}
|
||||
|
||||
// PinLsList is a set of pins with their type
|
||||
type PinLsList struct {
|
||||
Keys map[string]PinLsType
|
||||
}
|
||||
|
||||
// PinLsType contains the type of a pin
|
||||
type PinLsType struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
// PinLsObject contains the description of a pin
|
||||
type PinLsObject struct {
|
||||
Cid string `json:",omitempty"`
|
||||
Type string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func pinLsKeys(req *cmds.Request, typeStr string, n *core.IpfsNode, api coreiface.CoreAPI, emit func(value interface{}) error) error {
|
||||
mode, ok := pin.StringToMode(typeStr)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid pin mode '%s'", typeStr)
|
||||
}
|
||||
|
||||
enc, err := cmdenv.GetCidEncoder(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range req.Arguments {
|
||||
c, err := api.ResolvePath(req.Context, path.New(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pinType, pinned, err := n.Pinning.IsPinnedWithType(req.Context, c.Cid(), mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pinned {
|
||||
return fmt.Errorf("path '%s' is not pinned", p)
|
||||
}
|
||||
|
||||
switch pinType {
|
||||
case "direct", "indirect", "recursive", "internal":
|
||||
default:
|
||||
pinType = "indirect through " + pinType
|
||||
}
|
||||
|
||||
err = emit(&PinLsOutputWrapper{
|
||||
PinLsObject: PinLsObject{
|
||||
Type: pinType,
|
||||
Cid: enc.Encode(c.Cid()),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pinLsAll(req *cmds.Request, typeStr string, pinning pin.Pinner, dag ipld.DAGService, emit func(value interface{}) error) error {
|
||||
pinCh, errCh := coreapi.PinLsAll(req.Context, typeStr, pinning, dag)
|
||||
|
||||
enc, err := cmdenv.GetCidEncoder(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := req.Context
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case p, ok := <-pinCh:
|
||||
if !ok {
|
||||
break loop
|
||||
}
|
||||
if err := emit(&PinLsOutputWrapper{
|
||||
PinLsObject: PinLsObject{
|
||||
Type: p.Type(),
|
||||
Cid: enc.Encode(p.Path().Cid()),
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
err = <-errCh
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
pinUnpinOptionName = "unpin"
|
||||
)
|
||||
@ -385,15 +519,20 @@ var updatePinCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Update a recursive pin",
|
||||
ShortDescription: `
|
||||
Updates one pin to another, making sure that all objects in the new pin are
|
||||
local. Then removes the old pin. This is an optimized version of adding the
|
||||
new pin and removing the old one.
|
||||
Efficiently pins a new object based on differences from an existing one and,
|
||||
by default, removes the old pin.
|
||||
|
||||
This command is useful when the new pin contains many similarities or is a
|
||||
derivative of an existing one, particularly for large objects. This allows a more
|
||||
efficient DAG-traversal which fully skips already-pinned branches from the old
|
||||
object. As a requirement, the old object needs to be an existing recursive
|
||||
pin.
|
||||
`,
|
||||
},
|
||||
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("from-path", true, false, "Path to old object."),
|
||||
cmds.StringArg("to-path", true, false, "Path to new object to be pinned."),
|
||||
cmds.StringArg("to-path", true, false, "Path to a new object to be pinned."),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(pinUnpinOptionName, "Remove the old pin.").WithDefault(true),
|
||||
@ -471,8 +610,10 @@ var verifyPinCmd = &cmds.Command{
|
||||
explain: !quiet,
|
||||
includeOk: verbose,
|
||||
}
|
||||
out := pinVerify(req.Context, n, opts, enc)
|
||||
|
||||
out, err := pinVerify(req.Context, n, opts, enc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return res.Emit(out)
|
||||
},
|
||||
Type: PinVerifyRes{},
|
||||
@ -491,83 +632,6 @@ var verifyPinCmd = &cmds.Command{
|
||||
},
|
||||
}
|
||||
|
||||
type RefKeyObject struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
type RefKeyList struct {
|
||||
Keys map[string]RefKeyObject
|
||||
}
|
||||
|
||||
func pinLsKeys(ctx context.Context, args []string, typeStr string, n *core.IpfsNode, api coreiface.CoreAPI) (map[cid.Cid]RefKeyObject, error) {
|
||||
|
||||
mode, ok := pin.StringToMode(typeStr)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid pin mode '%s'", typeStr)
|
||||
}
|
||||
|
||||
keys := make(map[cid.Cid]RefKeyObject)
|
||||
|
||||
for _, p := range args {
|
||||
c, err := api.ResolvePath(ctx, path.New(p))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pinType, pinned, err := n.Pinning.IsPinnedWithType(c.Cid(), mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !pinned {
|
||||
return nil, fmt.Errorf("path '%s' is not pinned", p)
|
||||
}
|
||||
|
||||
switch pinType {
|
||||
case "direct", "indirect", "recursive", "internal":
|
||||
default:
|
||||
pinType = "indirect through " + pinType
|
||||
}
|
||||
keys[c.Cid()] = RefKeyObject{
|
||||
Type: pinType,
|
||||
}
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func pinLsAll(ctx context.Context, typeStr string, n *core.IpfsNode) (map[cid.Cid]RefKeyObject, error) {
|
||||
|
||||
keys := make(map[cid.Cid]RefKeyObject)
|
||||
|
||||
AddToResultKeys := func(keyList []cid.Cid, typeStr string) {
|
||||
for _, c := range keyList {
|
||||
keys[c] = RefKeyObject{
|
||||
Type: typeStr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if typeStr == "direct" || typeStr == "all" {
|
||||
AddToResultKeys(n.Pinning.DirectKeys(), "direct")
|
||||
}
|
||||
if typeStr == "indirect" || typeStr == "all" {
|
||||
set := cid.NewSet()
|
||||
for _, k := range n.Pinning.RecursiveKeys() {
|
||||
err := dag.EnumerateChildren(ctx, dag.GetLinksWithDAG(n.DAG), k, set.Visit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
AddToResultKeys(set.Keys(), "indirect")
|
||||
}
|
||||
if typeStr == "recursive" || typeStr == "all" {
|
||||
AddToResultKeys(n.Pinning.RecursiveKeys(), "recursive")
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// PinVerifyRes is the result returned for each pin checked in "pin verify"
|
||||
type PinVerifyRes struct {
|
||||
Cid string
|
||||
@ -591,13 +655,16 @@ type pinVerifyOpts struct {
|
||||
includeOk bool
|
||||
}
|
||||
|
||||
func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc cidenc.Encoder) <-chan interface{} {
|
||||
func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc cidenc.Encoder) (<-chan interface{}, error) {
|
||||
visited := make(map[cid.Cid]PinStatus)
|
||||
|
||||
bs := n.Blocks.Blockstore()
|
||||
DAG := dag.NewDAGService(bserv.New(bs, offline.Exchange(bs)))
|
||||
getLinks := dag.GetLinksWithDAG(DAG)
|
||||
recPins := n.Pinning.RecursiveKeys()
|
||||
recPins, err := n.Pinning.RecursiveKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var checkPin func(root cid.Cid) PinStatus
|
||||
checkPin = func(root cid.Cid) PinStatus {
|
||||
@ -653,7 +720,7 @@ func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc ci
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Format formats PinVerifyRes
|
||||
|
||||
@ -10,10 +10,9 @@ import (
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
|
||||
iaddr "github.com/ipfs/go-ipfs-addr"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
ping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
@ -207,13 +206,17 @@ trip latency information.
|
||||
func ParsePeerParam(text string) (ma.Multiaddr, peer.ID, error) {
|
||||
// Multiaddr
|
||||
if strings.HasPrefix(text, "/") {
|
||||
a, err := iaddr.ParseString(text)
|
||||
maddr, err := ma.NewMultiaddr(text)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return a.Transport(), a.ID(), nil
|
||||
transport, id := peer.SplitAddr(maddr)
|
||||
if id == "" {
|
||||
return nil, "", peer.ErrInvalidAddr
|
||||
}
|
||||
return transport, id, nil
|
||||
}
|
||||
// Raw peer ID
|
||||
p, err := peer.IDB58Decode(text)
|
||||
p, err := peer.Decode(text)
|
||||
return nil, p, err
|
||||
}
|
||||
|
||||
@ -7,15 +7,14 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
"github.com/ipfs/go-ipfs/namesys/resolve"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cidenc "github.com/ipfs/go-cidutil/cidenc"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
path "github.com/ipfs/go-path"
|
||||
iface "github.com/ipfs/interface-go-ipfs-core"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
)
|
||||
|
||||
var refsEncoderMap = cmds.EncoderMap{
|
||||
@ -75,7 +74,7 @@ NOTE: List all references recursively by using the flag '-r'.
|
||||
}
|
||||
|
||||
ctx := req.Context
|
||||
n, err := cmdenv.GetNode(env)
|
||||
api, err := cmdenv.GetApi(env, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -103,14 +102,14 @@ NOTE: List all references recursively by using the flag '-r'.
|
||||
format = "<src> -> <dst>"
|
||||
}
|
||||
|
||||
objs, err := objectsForPaths(ctx, n, req.Arguments)
|
||||
objs, err := objectsForPaths(ctx, api, req.Arguments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rw := RefWriter{
|
||||
res: res,
|
||||
DAG: n.DAG,
|
||||
DAG: api.Dag(),
|
||||
Ctx: ctx,
|
||||
Unique: unique,
|
||||
PrintFmt: format,
|
||||
@ -165,15 +164,10 @@ Displays the hashes of all local objects.
|
||||
Type: RefWrapper{},
|
||||
}
|
||||
|
||||
func objectsForPaths(ctx context.Context, n *core.IpfsNode, paths []string) ([]ipld.Node, error) {
|
||||
func objectsForPaths(ctx context.Context, n iface.CoreAPI, paths []string) ([]ipld.Node, error) {
|
||||
objects := make([]ipld.Node, len(paths))
|
||||
for i, sp := range paths {
|
||||
p, err := path.ParsePath(sp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o, err := resolve.Resolve(ctx, n.Namesys, n.Resolver, p)
|
||||
o, err := n.ResolveNode(ctx, path.New(sp))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -20,7 +19,6 @@ import (
|
||||
cid "github.com/ipfs/go-cid"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
)
|
||||
|
||||
type RepoVersion struct {
|
||||
@ -150,8 +148,8 @@ Version string The repo version.
|
||||
`,
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption(repoSizeOnlyOptionName, "Only report RepoSize and StorageMax."),
|
||||
cmds.BoolOption(repoHumanOptionName, "Print sizes in human readable format (e.g., 1K 234M 2G)"),
|
||||
cmds.BoolOption(repoSizeOnlyOptionName, "s", "Only report RepoSize and StorageMax."),
|
||||
cmds.BoolOption(repoHumanOptionName, "H", "Print sizes in human readable format (e.g., 1K 234M 2G)"),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
n, err := cmdenv.GetNode(env)
|
||||
@ -216,44 +214,11 @@ var repoFsckCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Remove repo lockfiles.",
|
||||
ShortDescription: `
|
||||
'ipfs repo fsck' is a plumbing command that will remove repo and level db
|
||||
lockfiles, as well as the api file. This command can only run when no ipfs
|
||||
daemons are running.
|
||||
'ipfs repo fsck' is now a no-op.
|
||||
`,
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
configRoot, err := cmdenv.GetConfigRoot(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dsPath, err := config.DataStorePath(configRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
|
||||
repoLockFile := filepath.Join(configRoot, fsrepo.LockFile)
|
||||
apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically
|
||||
|
||||
log.Infof("Removing repo lockfile: %s", repoLockFile)
|
||||
log.Infof("Removing datastore lockfile: %s", dsLockFile)
|
||||
log.Infof("Removing api file: %s", apiFile)
|
||||
|
||||
err = os.Remove(repoLockFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
err = os.Remove(dsLockFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
err = os.Remove(apiFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmds.EmitOnce(res, &MessageOutput{"Lockfiles have been removed.\n"})
|
||||
return cmds.EmitOnce(res, &MessageOutput{"`ipfs repo fsck` is deprecated and does nothing.\n"})
|
||||
},
|
||||
Type: MessageOutput{},
|
||||
Encoders: cmds.EncoderMap{
|
||||
|
||||
@ -144,7 +144,7 @@ var rootSubcommands = map[string]*cmds.Command{
|
||||
"swarm": SwarmCmd,
|
||||
"tar": TarCmd,
|
||||
"file": unixfs.UnixFSCmd,
|
||||
"update": ExternalBinary(),
|
||||
"update": ExternalBinary("Please see https://git.io/fjylH for installation instructions."),
|
||||
"urlstore": urlStoreCmd,
|
||||
"version": VersionCmd,
|
||||
"shutdown": daemonShutdownCmd,
|
||||
|
||||
@ -10,9 +10,9 @@ import (
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
metrics "github.com/libp2p/go-libp2p-metrics"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
)
|
||||
|
||||
var StatsCmd = &cmds.Command{
|
||||
@ -109,7 +109,7 @@ Example:
|
||||
|
||||
var pid peer.ID
|
||||
if pfound {
|
||||
checkpid, err := peer.IDB58Decode(pstr)
|
||||
checkpid, err := peer.Decode(pstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -15,12 +15,10 @@ import (
|
||||
repo "github.com/ipfs/go-ipfs/repo"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
|
||||
iaddr "github.com/ipfs/go-ipfs-addr"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
swarm "github.com/libp2p/go-libp2p-swarm"
|
||||
mafilter "github.com/libp2p/go-maddr-filter"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
@ -300,10 +298,11 @@ var swarmAddrsLocalCmd = &cmds.Command{
|
||||
}
|
||||
|
||||
var addrs []string
|
||||
p2pProtocolName := ma.ProtocolWithCode(ma.P_P2P).Name
|
||||
for _, addr := range maddrs {
|
||||
saddr := addr.String()
|
||||
if showid {
|
||||
saddr = path.Join(saddr, "ipfs", self.ID().Pretty())
|
||||
saddr = path.Join(saddr, p2pProtocolName, self.ID().Pretty())
|
||||
}
|
||||
addrs = append(addrs, saddr)
|
||||
}
|
||||
@ -356,7 +355,7 @@ var swarmConnectCmd = &cmds.Command{
|
||||
|
||||
The address format is an IPFS multiaddr:
|
||||
|
||||
ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
ipfs swarm connect /ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
@ -370,7 +369,7 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3
|
||||
|
||||
addrs := req.Arguments
|
||||
|
||||
pis, err := peersWithAddresses(req.Context, addrs)
|
||||
pis, err := parseAddresses(req.Context, addrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -401,7 +400,7 @@ var swarmDisconnectCmd = &cmds.Command{
|
||||
'ipfs swarm disconnect' closes a connection to a peer address. The address
|
||||
format is an IPFS multiaddr:
|
||||
|
||||
ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
|
||||
|
||||
The disconnect is not permanent; if ipfs needs to talk to that address later,
|
||||
it will reconnect.
|
||||
@ -416,19 +415,34 @@ it will reconnect.
|
||||
return err
|
||||
}
|
||||
|
||||
iaddrs, err := parseAddresses(req.Arguments)
|
||||
addrs, err := parseAddresses(req.Context, req.Arguments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output := make([]string, len(iaddrs))
|
||||
for i, addr := range iaddrs {
|
||||
output[i] = "disconnect " + addr.ID().Pretty()
|
||||
|
||||
if err := api.Swarm().Disconnect(req.Context, addr.Multiaddr()); err != nil {
|
||||
output[i] += " failure: " + err.Error()
|
||||
} else {
|
||||
output[i] += " success"
|
||||
output := make([]string, 0, len(addrs))
|
||||
for _, ainfo := range addrs {
|
||||
maddrs, err := peer.AddrInfoToP2pAddrs(&ainfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// FIXME: This will print:
|
||||
//
|
||||
// disconnect QmFoo success
|
||||
// disconnect QmFoo success
|
||||
// ...
|
||||
//
|
||||
// Once per address specified. However, I'm not sure of
|
||||
// a good backwards compat solution. Right now, I'm just
|
||||
// preserving the current behavior.
|
||||
for _, addr := range maddrs {
|
||||
msg := "disconnect " + ainfo.ID.Pretty()
|
||||
if err := api.Swarm().Disconnect(req.Context, addr); err != nil {
|
||||
msg += " failure: " + err.Error()
|
||||
} else {
|
||||
msg += " success"
|
||||
}
|
||||
output = append(output, msg)
|
||||
}
|
||||
}
|
||||
return cmds.EmitOnce(res, &stringList{output})
|
||||
@ -440,63 +454,15 @@ it will reconnect.
|
||||
}
|
||||
|
||||
// parseAddresses is a function that takes in a slice of string peer addresses
|
||||
// (multiaddr + peerid) and returns slices of multiaddrs and peerids.
|
||||
func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) {
|
||||
iaddrs = make([]iaddr.IPFSAddr, len(addrs))
|
||||
for i, saddr := range addrs {
|
||||
iaddrs[i], err = iaddr.ParseString(saddr)
|
||||
if err != nil {
|
||||
return nil, cmds.ClientError("invalid peer address: " + err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseMultiaddrs is a function that takes in a slice of peer multiaddr
|
||||
// and returns slices of multiaddrs and peerids
|
||||
func parseMultiaddrs(maddrs []ma.Multiaddr) (iaddrs []iaddr.IPFSAddr, err error) {
|
||||
iaddrs = make([]iaddr.IPFSAddr, len(maddrs))
|
||||
for i, maddr := range maddrs {
|
||||
iaddrs[i], err = iaddr.ParseMultiaddr(maddr)
|
||||
if err != nil {
|
||||
return nil, cmds.ClientError("invalid peer address: " + err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// peersWithAddresses is a function that takes in a slice of string peer addresses
|
||||
// (multiaddr + peerid) and returns a slice of properly constructed peers
|
||||
func peersWithAddresses(ctx context.Context, addrs []string) ([]pstore.PeerInfo, error) {
|
||||
func parseAddresses(ctx context.Context, addrs []string) ([]peer.AddrInfo, error) {
|
||||
// resolve addresses
|
||||
maddrs, err := resolveAddresses(ctx, addrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iaddrs, err := parseMultiaddrs(maddrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peers := make(map[peer.ID][]ma.Multiaddr, len(iaddrs))
|
||||
for _, iaddr := range iaddrs {
|
||||
id := iaddr.ID()
|
||||
current, ok := peers[id]
|
||||
if tpt := iaddr.Transport(); tpt != nil {
|
||||
peers[id] = append(current, tpt)
|
||||
} else if !ok {
|
||||
peers[id] = nil
|
||||
}
|
||||
}
|
||||
pis := make([]pstore.PeerInfo, 0, len(peers))
|
||||
for id, maddrs := range peers {
|
||||
pis = append(pis, pstore.PeerInfo{
|
||||
ID: id,
|
||||
Addrs: maddrs,
|
||||
})
|
||||
}
|
||||
return pis, nil
|
||||
return peer.AddrInfosFromP2pAddrs(maddrs...)
|
||||
}
|
||||
|
||||
// resolveAddresses resolves addresses parallelly
|
||||
@ -532,7 +498,7 @@ func resolveAddresses(ctx context.Context, addrs []string) ([]ma.Multiaddr, erro
|
||||
// filter out addresses that still doesn't end in `ipfs/Qm...`
|
||||
found := 0
|
||||
for _, raddr := range raddrs {
|
||||
if _, last := ma.SplitLast(raddr); last.Protocol().Code == ma.P_IPFS {
|
||||
if _, last := ma.SplitLast(raddr); last != nil && last.Protocol().Code == ma.P_IPFS {
|
||||
maddrC <- raddr
|
||||
found++
|
||||
}
|
||||
@ -620,8 +586,6 @@ var swarmFiltersAddCmd = &cmds.Command{
|
||||
Tagline: "Add an address filter.",
|
||||
ShortDescription: `
|
||||
'ipfs swarm filters add' will add an address filter to the daemons swarm.
|
||||
Filters applied this way will not persist daemon reboots, to achieve that,
|
||||
add your filters to the ipfs config file.
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
@ -684,8 +648,6 @@ var swarmFiltersRmCmd = &cmds.Command{
|
||||
Tagline: "Remove an address filter.",
|
||||
ShortDescription: `
|
||||
'ipfs swarm filters rm' will remove an address filter from the daemons swarm.
|
||||
Filters removed this way will not persist daemon reboots, to achieve that,
|
||||
remove your filters from the ipfs config file.
|
||||
`,
|
||||
},
|
||||
Arguments: []cmds.Argument{
|
||||
|
||||
@ -5,12 +5,11 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
"github.com/ipfs/go-ipfs/namesys/resolve"
|
||||
tar "github.com/ipfs/go-ipfs/tar"
|
||||
|
||||
"github.com/ipfs/go-ipfs-cmds"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
"github.com/ipfs/go-path"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
)
|
||||
|
||||
var TarCmd = &cmds.Command{
|
||||
@ -37,7 +36,7 @@ represent it.
|
||||
cmds.FileArg("file", true, false, "Tar file to add.").EnableStdin(),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
nd, err := cmdenv.GetNode(env)
|
||||
api, err := cmdenv.GetApi(env, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -53,7 +52,7 @@ represent it.
|
||||
return err
|
||||
}
|
||||
|
||||
node, err := tar.ImportTar(req.Context, file, nd.DAG)
|
||||
node, err := tar.ImportTar(req.Context, file, api.Dag())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -86,17 +85,12 @@ var tarCatCmd = &cmds.Command{
|
||||
cmds.StringArg("path", true, false, "ipfs path of archive to export.").EnableStdin(),
|
||||
},
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
nd, err := cmdenv.GetNode(env)
|
||||
api, err := cmdenv.GetApi(env, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := path.ParsePath(req.Arguments[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
root, err := resolve.Resolve(req.Context, nd.Namesys, nd.Resolver, p)
|
||||
root, err := api.ResolveNode(req.Context, path.New(req.Arguments[0]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -106,7 +100,7 @@ var tarCatCmd = &cmds.Command{
|
||||
return dag.ErrNotProtobuf
|
||||
}
|
||||
|
||||
r, err := tar.ExportTar(req.Context, rootpb, nd.DAG)
|
||||
r, err := tar.ExportTar(req.Context, rootpb, api.Dag())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
filestore "github.com/ipfs/go-filestore"
|
||||
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
|
||||
filestore "github.com/ipfs/go-ipfs/filestore"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
|
||||
@ -54,21 +54,25 @@ var VersionCmd = &cmds.Command{
|
||||
},
|
||||
Encoders: cmds.EncoderMap{
|
||||
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, version *VersionOutput) error {
|
||||
commit, _ := req.Options[versionCommitOptionName].(bool)
|
||||
commitTxt := ""
|
||||
if commit {
|
||||
commitTxt = "-" + version.Commit
|
||||
}
|
||||
|
||||
all, _ := req.Options[versionAllOptionName].(bool)
|
||||
if all {
|
||||
out := fmt.Sprintf("go-ipfs version: %s-%s\n"+
|
||||
ver := version.Version
|
||||
if version.Commit != "" {
|
||||
ver += "-" + version.Commit
|
||||
}
|
||||
out := fmt.Sprintf("go-ipfs version: %s\n"+
|
||||
"Repo version: %s\nSystem version: %s\nGolang version: %s\n",
|
||||
version.Version, version.Commit, version.Repo, version.System, version.Golang)
|
||||
ver, version.Repo, version.System, version.Golang)
|
||||
fmt.Fprint(w, out)
|
||||
return nil
|
||||
}
|
||||
|
||||
commit, _ := req.Options[versionCommitOptionName].(bool)
|
||||
commitTxt := ""
|
||||
if commit && version.Commit != "" {
|
||||
commitTxt = "-" + version.Commit
|
||||
}
|
||||
|
||||
repo, _ := req.Options[versionRepoOptionName].(bool)
|
||||
if repo {
|
||||
fmt.Fprintln(w, version.Repo)
|
||||
|
||||
113
core/core.go
113
core/core.go
@ -13,51 +13,45 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
version "github.com/ipfs/go-ipfs"
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/ipfs/go-ipfs-pinner"
|
||||
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-graphsync"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
exchange "github.com/ipfs/go-ipfs-exchange-interface"
|
||||
"github.com/ipfs/go-ipfs-provider"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log"
|
||||
mfs "github.com/ipfs/go-mfs"
|
||||
resolver "github.com/ipfs/go-path/resolver"
|
||||
goprocess "github.com/jbenet/goprocess"
|
||||
connmgr "github.com/libp2p/go-libp2p-core/connmgr"
|
||||
ic "github.com/libp2p/go-libp2p-core/crypto"
|
||||
p2phost "github.com/libp2p/go-libp2p-core/host"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
ddht "github.com/libp2p/go-libp2p-kad-dht/dual"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
psrouter "github.com/libp2p/go-libp2p-pubsub-router"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
"github.com/libp2p/go-libp2p/p2p/discovery"
|
||||
p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/bootstrap"
|
||||
"github.com/ipfs/go-ipfs/core/node"
|
||||
"github.com/ipfs/go-ipfs/core/node/libp2p"
|
||||
"github.com/ipfs/go-ipfs/filestore"
|
||||
"github.com/ipfs/go-ipfs/fuse/mount"
|
||||
"github.com/ipfs/go-ipfs/namesys"
|
||||
ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
|
||||
"github.com/ipfs/go-ipfs/p2p"
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
"github.com/ipfs/go-ipfs/provider"
|
||||
"github.com/ipfs/go-ipfs/repo"
|
||||
rp "github.com/ipfs/go-ipfs/reprovide"
|
||||
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
exchange "github.com/ipfs/go-ipfs-exchange-interface"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/ipfs/go-mfs"
|
||||
"github.com/ipfs/go-path/resolver"
|
||||
"github.com/jbenet/goprocess"
|
||||
autonat "github.com/libp2p/go-libp2p-autonat-svc"
|
||||
ic "github.com/libp2p/go-libp2p-crypto"
|
||||
p2phost "github.com/libp2p/go-libp2p-host"
|
||||
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
metrics "github.com/libp2p/go-libp2p-metrics"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
psrouter "github.com/libp2p/go-libp2p-pubsub-router"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
"github.com/libp2p/go-libp2p/p2p/discovery"
|
||||
p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
||||
)
|
||||
|
||||
var log = logging.Logger("core")
|
||||
|
||||
func init() {
|
||||
identify.ClientVersion = "go-ipfs/" + version.CurrentVersionNumber + "/" + version.CurrentCommit
|
||||
}
|
||||
|
||||
// IpfsNode is IPFS Core module. It represents an IPFS instance.
|
||||
type IpfsNode struct {
|
||||
|
||||
@ -73,33 +67,32 @@ type IpfsNode struct {
|
||||
PNetFingerprint libp2p.PNetFingerprint `optional:"true"` // fingerprint of private network
|
||||
|
||||
// Services
|
||||
Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances
|
||||
Blockstore bstore.GCBlockstore // the block store (lower level)
|
||||
Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore
|
||||
BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping
|
||||
GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc
|
||||
Blocks bserv.BlockService // the block service, get/add blocks.
|
||||
DAG ipld.DAGService // the merkle dag service, get/add objects.
|
||||
Resolver *resolver.Resolver // the path resolution system
|
||||
Reporter metrics.Reporter `optional:"true"`
|
||||
Discovery discovery.Service `optional:"true"`
|
||||
Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances
|
||||
Blockstore bstore.GCBlockstore // the block store (lower level)
|
||||
Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore
|
||||
BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping
|
||||
GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc
|
||||
Blocks bserv.BlockService // the block service, get/add blocks.
|
||||
DAG ipld.DAGService // the merkle dag service, get/add objects.
|
||||
Resolver *resolver.Resolver // the path resolution system
|
||||
Reporter *metrics.BandwidthCounter `optional:"true"`
|
||||
Discovery discovery.Service `optional:"true"`
|
||||
FilesRoot *mfs.Root
|
||||
RecordValidator record.Validator
|
||||
|
||||
// Online
|
||||
PeerHost p2phost.Host `optional:"true"` // the network host (server+client)
|
||||
Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper
|
||||
Routing routing.IpfsRouting `optional:"true"` // the routing system. recommend ipfs-dht
|
||||
Exchange exchange.Interface // the block exchange + strategy (bitswap)
|
||||
Namesys namesys.NameSystem // the name system, resolves paths to hashes
|
||||
Provider provider.Provider // the value provider system
|
||||
Reprovider *rp.Reprovider `optional:"true"` // the value reprovider system
|
||||
IpnsRepub *ipnsrp.Republisher `optional:"true"`
|
||||
PeerHost p2phost.Host `optional:"true"` // the network host (server+client)
|
||||
Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper
|
||||
Routing routing.Routing `optional:"true"` // the routing system. recommend ipfs-dht
|
||||
Exchange exchange.Interface // the block exchange + strategy (bitswap)
|
||||
Namesys namesys.NameSystem // the name system, resolves paths to hashes
|
||||
Provider provider.System // the value provider system
|
||||
IpnsRepub *ipnsrp.Republisher `optional:"true"`
|
||||
GraphExchange graphsync.GraphExchange `optional:"true"`
|
||||
|
||||
AutoNAT *autonat.AutoNATService `optional:"true"`
|
||||
PubSub *pubsub.PubSub `optional:"true"`
|
||||
PSRouter *psrouter.PubsubValueStore `optional:"true"`
|
||||
DHT *dht.IpfsDHT `optional:"true"`
|
||||
DHT *ddht.DHT `optional:"true"`
|
||||
P2P *p2p.P2P `optional:"true"`
|
||||
|
||||
Process goprocess.Process
|
||||
@ -147,10 +140,10 @@ func (n *IpfsNode) Bootstrap(cfg bootstrap.BootstrapConfig) error {
|
||||
// if the caller did not specify a bootstrap peer function, get the
|
||||
// freshest bootstrap peers from config. this responds to live changes.
|
||||
if cfg.BootstrapPeers == nil {
|
||||
cfg.BootstrapPeers = func() []pstore.PeerInfo {
|
||||
cfg.BootstrapPeers = func() []peer.AddrInfo {
|
||||
ps, err := n.loadBootstrapPeers()
|
||||
if err != nil {
|
||||
log.Warning("failed to parse bootstrap peers from config")
|
||||
log.Warn("failed to parse bootstrap peers from config")
|
||||
return nil
|
||||
}
|
||||
return ps
|
||||
@ -162,17 +155,13 @@ func (n *IpfsNode) Bootstrap(cfg bootstrap.BootstrapConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
|
||||
func (n *IpfsNode) loadBootstrapPeers() ([]peer.AddrInfo, error) {
|
||||
cfg, err := n.Repo.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsed, err := cfg.BootstrapPeers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bootstrap.Peers.ToPeerInfos(parsed), nil
|
||||
return cfg.BootstrapPeers()
|
||||
}
|
||||
|
||||
type ConstructPeerHostOpts struct {
|
||||
@ -180,5 +169,5 @@ type ConstructPeerHostOpts struct {
|
||||
DisableNatPortMap bool
|
||||
DisableRelay bool
|
||||
EnableRelayHop bool
|
||||
ConnectionManager ifconnmgr.ConnManager
|
||||
ConnectionManager connmgr.ConnManager
|
||||
}
|
||||
|
||||
@ -7,14 +7,14 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
util "github.com/ipfs/go-ipfs/blocks/blockstoreutil"
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
pin "github.com/ipfs/go-ipfs-pinner"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
|
||||
util "github.com/ipfs/go-ipfs/blocks/blockstoreutil"
|
||||
)
|
||||
|
||||
type BlockAPI CoreAPI
|
||||
@ -56,7 +56,7 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc
|
||||
|
||||
if settings.Pin {
|
||||
api.pinning.PinWithMode(b.Cid(), pin.Recursive)
|
||||
if err := api.pinning.Flush(); err != nil {
|
||||
if err := api.pinning.Flush(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -91,7 +91,7 @@ func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRm
|
||||
cids := []cid.Cid{rp.Cid()}
|
||||
o := util.RmBlocksOpts{Force: settings.Force}
|
||||
|
||||
out, err := util.RmBlocks(api.blockstore, api.pinning, cids, o)
|
||||
out, err := util.RmBlocks(ctx, api.blockstore, api.pinning, cids, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -18,30 +18,30 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
"github.com/ipfs/go-ipfs/core/node"
|
||||
"github.com/ipfs/go-ipfs/namesys"
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
"github.com/ipfs/go-ipfs/provider"
|
||||
"github.com/ipfs/go-ipfs/repo"
|
||||
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-ipfs-blockstore"
|
||||
"github.com/ipfs/go-ipfs-exchange-interface"
|
||||
offlinexch "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
"github.com/ipfs/go-ipfs-pinner"
|
||||
"github.com/ipfs/go-ipfs-provider"
|
||||
offlineroute "github.com/ipfs/go-ipfs-routing/offline"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
"github.com/ipfs/interface-go-ipfs-core/options"
|
||||
ci "github.com/libp2p/go-libp2p-crypto"
|
||||
p2phost "github.com/libp2p/go-libp2p-host"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
p2phost "github.com/libp2p/go-libp2p-core/host"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
"github.com/libp2p/go-libp2p-routing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
"github.com/ipfs/go-ipfs/core/node"
|
||||
"github.com/ipfs/go-ipfs/namesys"
|
||||
"github.com/ipfs/go-ipfs/repo"
|
||||
)
|
||||
|
||||
var log = logging.Logger("core/coreapi")
|
||||
@ -66,9 +66,9 @@ type CoreAPI struct {
|
||||
exchange exchange.Interface
|
||||
|
||||
namesys namesys.NameSystem
|
||||
routing routing.IpfsRouting
|
||||
routing routing.Routing
|
||||
|
||||
provider provider.Provider
|
||||
provider provider.System
|
||||
|
||||
pubSub *pubsub.PubSub
|
||||
|
||||
|
||||
@ -3,9 +3,8 @@ package coreapi
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-ipfs-pinner"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
@ -26,7 +25,7 @@ func (adder *pinningAdder) Add(ctx context.Context, nd ipld.Node) error {
|
||||
|
||||
adder.pinning.PinWithMode(nd.Cid(), pin.Recursive)
|
||||
|
||||
return adder.pinning.Flush()
|
||||
return adder.pinning.Flush(ctx)
|
||||
}
|
||||
|
||||
func (adder *pinningAdder) AddMany(ctx context.Context, nds []ipld.Node) error {
|
||||
@ -45,7 +44,7 @@ func (adder *pinningAdder) AddMany(ctx context.Context, nds []ipld.Node) error {
|
||||
}
|
||||
}
|
||||
|
||||
return adder.pinning.Flush()
|
||||
return adder.pinning.Flush(ctx)
|
||||
}
|
||||
|
||||
func (api *dagAPI) Pinning() ipld.NodeAdder {
|
||||
|
||||
@ -13,28 +13,27 @@ import (
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
)
|
||||
|
||||
type DhtAPI CoreAPI
|
||||
|
||||
func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (pstore.PeerInfo, error) {
|
||||
func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) {
|
||||
err := api.checkOnline(false)
|
||||
if err != nil {
|
||||
return pstore.PeerInfo{}, err
|
||||
return peer.AddrInfo{}, err
|
||||
}
|
||||
|
||||
pi, err := api.routing.FindPeer(ctx, peer.ID(p))
|
||||
if err != nil {
|
||||
return pstore.PeerInfo{}, err
|
||||
return peer.AddrInfo{}, err
|
||||
}
|
||||
|
||||
return pi, nil
|
||||
}
|
||||
|
||||
func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopts.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) {
|
||||
func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopts.DhtFindProvidersOption) (<-chan peer.AddrInfo, error) {
|
||||
settings, err := caopts.DhtFindProvidersOptions(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -98,7 +97,7 @@ func (api *DhtAPI) Provide(ctx context.Context, path path.Path, opts ...caopts.D
|
||||
return nil
|
||||
}
|
||||
|
||||
func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []cid.Cid) error {
|
||||
func provideKeys(ctx context.Context, r routing.Routing, cids []cid.Cid) error {
|
||||
for _, c := range cids {
|
||||
err := r.Provide(ctx, c, true)
|
||||
if err != nil {
|
||||
@ -108,14 +107,14 @@ func provideKeys(ctx context.Context, r routing.IpfsRouting, cids []cid.Cid) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func provideKeysRec(ctx context.Context, r routing.IpfsRouting, bs blockstore.Blockstore, cids []cid.Cid) error {
|
||||
func provideKeysRec(ctx context.Context, r routing.Routing, bs blockstore.Blockstore, cids []cid.Cid) error {
|
||||
provided := cidutil.NewStreamingSet()
|
||||
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
dserv := dag.NewDAGService(blockservice.New(bs, offline.Exchange(bs)))
|
||||
for _, c := range cids {
|
||||
err := dag.EnumerateChildrenAsync(ctx, dag.GetLinksDirect(dserv), c, provided.Visitor(ctx))
|
||||
err := dag.Walk(ctx, dag.GetLinksDirect(dserv), c, provided.Visitor(ctx))
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
crypto "github.com/libp2p/go-libp2p-crypto"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
crypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
type KeyAPI CoreAPI
|
||||
|
||||
@ -13,9 +13,8 @@ import (
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
"github.com/libp2p/go-libp2p-crypto"
|
||||
ci "github.com/libp2p/go-libp2p-crypto"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
type NameAPI CoreAPI
|
||||
@ -139,11 +138,17 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
|
||||
return p, err
|
||||
}
|
||||
|
||||
func keylookup(self ci.PrivKey, kstore keystore.Keystore, k string) (crypto.PrivKey, error) {
|
||||
func keylookup(self ci.PrivKey, kstore keystore.Keystore, k string) (ci.PrivKey, error) {
|
||||
////////////////////
|
||||
// Lookup by name //
|
||||
////////////////////
|
||||
|
||||
// First, lookup self.
|
||||
if k == "self" {
|
||||
return self, nil
|
||||
}
|
||||
|
||||
// Then, look in the keystore.
|
||||
res, err := kstore.Get(k)
|
||||
if res != nil {
|
||||
return res, nil
|
||||
@ -158,20 +163,36 @@ func keylookup(self ci.PrivKey, kstore keystore.Keystore, k string) (crypto.Priv
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// Lookup by ID //
|
||||
//////////////////
|
||||
targetPid, err := peer.Decode(k)
|
||||
if err != nil {
|
||||
return nil, keystore.ErrNoSuchKey
|
||||
}
|
||||
|
||||
// First, check self.
|
||||
pid, err := peer.IDFromPrivateKey(self)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to determine peer ID for private key: %w", err)
|
||||
}
|
||||
if pid == targetPid {
|
||||
return self, nil
|
||||
}
|
||||
|
||||
// Then, look in the keystore.
|
||||
for _, key := range keys {
|
||||
privKey, err := kstore.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKey := privKey.GetPublic()
|
||||
|
||||
pid, err := peer.IDFromPublicKey(pubKey)
|
||||
pid, err := peer.IDFromPrivateKey(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pid.Pretty() == k {
|
||||
if targetPid == pid {
|
||||
return privKey, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,12 +11,11 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ipfs/go-ipfs/dagutils"
|
||||
"github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-ipfs-pinner"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
"github.com/ipfs/go-merkledag/dagutils"
|
||||
ft "github.com/ipfs/go-unixfs"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
@ -119,7 +118,7 @@ func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Obj
|
||||
|
||||
if options.Pin {
|
||||
api.pinning.PinWithMode(dagnode.Cid(), pin.Recursive)
|
||||
err = api.pinning.Flush()
|
||||
err = api.pinning.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -307,7 +306,7 @@ func (api *ObjectAPI) Diff(ctx context.Context, before ipath.Path, after ipath.P
|
||||
out := make([]coreiface.ObjectChange, len(changes))
|
||||
for i, change := range changes {
|
||||
out[i] = coreiface.ObjectChange{
|
||||
Type: change.Type,
|
||||
Type: coreiface.ChangeType(change.Type),
|
||||
Path: change.Path,
|
||||
}
|
||||
|
||||
@ -339,7 +338,7 @@ func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error)
|
||||
}
|
||||
dagnode.SetData(data)
|
||||
default:
|
||||
return nil, fmt.Errorf("unkown data field encoding")
|
||||
return nil, fmt.Errorf("unknown data field encoding")
|
||||
}
|
||||
|
||||
links := make([]*ipld.Link, len(nd.Links))
|
||||
|
||||
@ -3,14 +3,15 @@ package coreapi
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-cid"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
merkledag "github.com/ipfs/go-merkledag"
|
||||
pin "github.com/ipfs/go-ipfs-pinner"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
path "github.com/ipfs/interface-go-ipfs-core/path"
|
||||
"github.com/ipfs/interface-go-ipfs-core/path"
|
||||
)
|
||||
|
||||
type PinAPI CoreAPI
|
||||
@ -37,7 +38,7 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
|
||||
return err
|
||||
}
|
||||
|
||||
return api.pinning.Flush()
|
||||
return api.pinning.Flush(ctx)
|
||||
}
|
||||
|
||||
func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) ([]coreiface.Pin, error) {
|
||||
@ -75,7 +76,7 @@ func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOpti
|
||||
return err
|
||||
}
|
||||
|
||||
return api.pinning.Flush()
|
||||
return api.pinning.Flush(ctx)
|
||||
}
|
||||
|
||||
func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opts ...caopts.PinUpdateOption) error {
|
||||
@ -101,7 +102,7 @@ func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opt
|
||||
return err
|
||||
}
|
||||
|
||||
return api.pinning.Flush()
|
||||
return api.pinning.Flush(ctx)
|
||||
}
|
||||
|
||||
type pinStatus struct {
|
||||
@ -137,7 +138,10 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
|
||||
bs := api.blockstore
|
||||
DAG := merkledag.NewDAGService(bserv.New(bs, offline.Exchange(bs)))
|
||||
getLinks := merkledag.GetLinksWithDAG(DAG)
|
||||
recPins := api.pinning.RecursiveKeys()
|
||||
recPins, err := api.pinning.RecursiveKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var checkPin func(root cid.Cid) *pinStatus
|
||||
checkPin = func(root cid.Cid) *pinStatus {
|
||||
@ -191,41 +195,122 @@ func (p *pinInfo) Type() string {
|
||||
}
|
||||
|
||||
func (api *PinAPI) pinLsAll(typeStr string, ctx context.Context) ([]coreiface.Pin, error) {
|
||||
pinCh, errCh := PinLsAll(ctx, typeStr, api.pinning, api.dag)
|
||||
|
||||
keys := make(map[cid.Cid]*pinInfo)
|
||||
var pins []coreiface.Pin
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case p, ok := <-pinCh:
|
||||
if !ok {
|
||||
break loop
|
||||
}
|
||||
pins = append(pins, p)
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
err := <-errCh
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
AddToResultKeys := func(keyList []cid.Cid, typeStr string) {
|
||||
return pins, nil
|
||||
}
|
||||
|
||||
// PinLsAll is an internal function for returning a list of pins
|
||||
func PinLsAll(ctx context.Context, typeStr string, pin pin.Pinner, dag ipld.DAGService) (chan coreiface.Pin, chan error) {
|
||||
ch := make(chan coreiface.Pin, 32)
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
keys := cid.NewSet()
|
||||
AddToResultKeys := func(keyList []cid.Cid, typeStr string) error {
|
||||
for _, c := range keyList {
|
||||
keys[c] = &pinInfo{
|
||||
pinType: typeStr,
|
||||
path: path.IpldPath(c),
|
||||
if keys.Visit(c) {
|
||||
select {
|
||||
case ch <- &pinInfo{
|
||||
pinType: typeStr,
|
||||
path: path.IpldPath(c),
|
||||
}:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if typeStr == "direct" || typeStr == "all" {
|
||||
AddToResultKeys(api.pinning.DirectKeys(), "direct")
|
||||
}
|
||||
if typeStr == "indirect" || typeStr == "all" {
|
||||
set := cid.NewSet()
|
||||
for _, k := range api.pinning.RecursiveKeys() {
|
||||
err := merkledag.EnumerateChildren(ctx, merkledag.GetLinksWithDAG(api.dag), k, set.Visit)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
defer close(errCh)
|
||||
if typeStr == "direct" || typeStr == "all" {
|
||||
dkeys, err := pin.DirectKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := AddToResultKeys(dkeys, "direct"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
AddToResultKeys(set.Keys(), "indirect")
|
||||
}
|
||||
if typeStr == "recursive" || typeStr == "all" {
|
||||
AddToResultKeys(api.pinning.RecursiveKeys(), "recursive")
|
||||
}
|
||||
if typeStr == "recursive" || typeStr == "all" {
|
||||
rkeys, err := pin.RecursiveKeys(ctx)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := AddToResultKeys(rkeys, "recursive"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
if typeStr == "indirect" || typeStr == "all" {
|
||||
rkeys, err := pin.RecursiveKeys(ctx)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
out := make([]coreiface.Pin, 0, len(keys))
|
||||
for _, v := range keys {
|
||||
out = append(out, v)
|
||||
}
|
||||
// If we're only listing indirect pins, we need to
|
||||
// explicitly mark direct/recursive pins so we don't
|
||||
// send them.
|
||||
if typeStr == "indirect" {
|
||||
dkeys, err := pin.DirectKeys(ctx)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
return out, nil
|
||||
for _, k := range dkeys {
|
||||
keys.Add(k)
|
||||
}
|
||||
for _, k := range rkeys {
|
||||
keys.Add(k)
|
||||
}
|
||||
}
|
||||
|
||||
indirectKeys := cid.NewSet()
|
||||
for _, k := range rkeys {
|
||||
err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), k, func(c cid.Cid) bool {
|
||||
r := indirectKeys.Visit(c)
|
||||
if r {
|
||||
if err := AddToResultKeys([]cid.Cid{c}, "indirect"); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return r
|
||||
}, merkledag.SkipRoot(), merkledag.Concurrent())
|
||||
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch, errCh
|
||||
}
|
||||
|
||||
func (api *PinAPI) core() coreiface.CoreAPI {
|
||||
|
||||
@ -10,11 +10,10 @@ import (
|
||||
cid "github.com/ipfs/go-cid"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
caopts "github.com/ipfs/interface-go-ipfs-core/options"
|
||||
p2phost "github.com/libp2p/go-libp2p-host"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
p2phost "github.com/libp2p/go-libp2p-core/host"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
routing "github.com/libp2p/go-libp2p-routing"
|
||||
)
|
||||
|
||||
type PubSubAPI CoreAPI
|
||||
@ -57,6 +56,7 @@ func (api *PubSubAPI) Publish(ctx context.Context, topic string, data []byte) er
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint deprecated
|
||||
return api.pubSub.Publish(topic, data)
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//nolint deprecated
|
||||
sub, err := api.pubSub.Subscribe(topic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -93,7 +94,7 @@ func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopt
|
||||
return &pubSubSubscription{cancel, sub}, nil
|
||||
}
|
||||
|
||||
func connectToPubSubPeers(ctx context.Context, r routing.IpfsRouting, ph p2phost.Host, cid cid.Cid) {
|
||||
func connectToPubSubPeers(ctx context.Context, r routing.Routing, ph p2phost.Host, cid cid.Cid) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@ -101,7 +102,7 @@ func connectToPubSubPeers(ctx context.Context, r routing.IpfsRouting, ph p2phost
|
||||
var wg sync.WaitGroup
|
||||
for p := range provs {
|
||||
wg.Add(1)
|
||||
go func(pi pstore.PeerInfo) {
|
||||
go func(pi peer.AddrInfo) {
|
||||
defer wg.Done()
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
@ -117,7 +118,7 @@ func connectToPubSubPeers(ctx context.Context, r routing.IpfsRouting, ph p2phost
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (api *PubSubAPI) checkNode() (routing.IpfsRouting, error) {
|
||||
func (api *PubSubAPI) checkNode() (routing.Routing, error) {
|
||||
if api.pubSub == nil {
|
||||
return nil, errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use.")
|
||||
}
|
||||
|
||||
@ -5,13 +5,11 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
iaddr "github.com/ipfs/go-ipfs-addr"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
net "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-protocol"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
swarm "github.com/libp2p/go-libp2p-swarm"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
@ -20,8 +18,8 @@ type SwarmAPI CoreAPI
|
||||
|
||||
type connInfo struct {
|
||||
peerstore pstore.Peerstore
|
||||
conn net.Conn
|
||||
dir net.Direction
|
||||
conn inet.Conn
|
||||
dir inet.Direction
|
||||
|
||||
addr ma.Multiaddr
|
||||
peer peer.ID
|
||||
@ -31,7 +29,7 @@ type connInfo struct {
|
||||
const connectionManagerTag = "user-connect"
|
||||
const connectionManagerWeight = 100
|
||||
|
||||
func (api *SwarmAPI) Connect(ctx context.Context, pi pstore.PeerInfo) error {
|
||||
func (api *SwarmAPI) Connect(ctx context.Context, pi peer.AddrInfo) error {
|
||||
if api.peerHost == nil {
|
||||
return coreiface.ErrOffline
|
||||
}
|
||||
@ -53,34 +51,29 @@ func (api *SwarmAPI) Disconnect(ctx context.Context, addr ma.Multiaddr) error {
|
||||
return coreiface.ErrOffline
|
||||
}
|
||||
|
||||
ia, err := iaddr.ParseMultiaddr(ma.Multiaddr(addr))
|
||||
if err != nil {
|
||||
return err
|
||||
taddr, id := peer.SplitAddr(addr)
|
||||
if id == "" {
|
||||
return peer.ErrInvalidAddr
|
||||
}
|
||||
|
||||
taddr := ia.Transport()
|
||||
id := ia.ID()
|
||||
net := api.peerHost.Network()
|
||||
|
||||
if taddr == nil {
|
||||
if net.Connectedness(id) != inet.Connected {
|
||||
return coreiface.ErrNotConnected
|
||||
} else if err := net.ClosePeer(id); err != nil {
|
||||
}
|
||||
if err := net.ClosePeer(id); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for _, conn := range net.ConnsToPeer(id) {
|
||||
if !conn.RemoteMultiaddr().Equal(taddr) {
|
||||
continue
|
||||
}
|
||||
|
||||
return conn.Close()
|
||||
return nil
|
||||
}
|
||||
for _, conn := range net.ConnsToPeer(id) {
|
||||
if !conn.RemoteMultiaddr().Equal(taddr) {
|
||||
continue
|
||||
}
|
||||
|
||||
return coreiface.ErrConnNotFound
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
return coreiface.ErrConnNotFound
|
||||
}
|
||||
|
||||
func (api *SwarmAPI) KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) {
|
||||
@ -159,7 +152,7 @@ func (ci *connInfo) Address() ma.Multiaddr {
|
||||
return ci.addr
|
||||
}
|
||||
|
||||
func (ci *connInfo) Direction() net.Direction {
|
||||
func (ci *connInfo) Direction() inet.Direction {
|
||||
return ci.dir
|
||||
}
|
||||
|
||||
|
||||
@ -8,12 +8,12 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core/bootstrap"
|
||||
"github.com/ipfs/go-ipfs/filestore"
|
||||
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
"github.com/ipfs/go-ipfs/core/bootstrap"
|
||||
"github.com/ipfs/go-ipfs/core/coreapi"
|
||||
mock "github.com/ipfs/go-ipfs/core/mock"
|
||||
"github.com/ipfs/go-ipfs/core/node/libp2p"
|
||||
"github.com/ipfs/go-ipfs/keystore"
|
||||
"github.com/ipfs/go-ipfs/repo"
|
||||
|
||||
@ -22,9 +22,8 @@ import (
|
||||
"github.com/ipfs/go-ipfs-config"
|
||||
coreiface "github.com/ipfs/interface-go-ipfs-core"
|
||||
"github.com/ipfs/interface-go-ipfs-core/tests"
|
||||
ci "github.com/libp2p/go-libp2p-crypto"
|
||||
"github.com/libp2p/go-libp2p-peer"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||
)
|
||||
|
||||
@ -41,7 +40,7 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int)
|
||||
for i := 0; i < n; i++ {
|
||||
var ident config.Identity
|
||||
if fullIdentity {
|
||||
sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512)
|
||||
sk, pk, err := ci.GenerateKeyPair(ci.RSA, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -67,7 +66,7 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int)
|
||||
}
|
||||
|
||||
c := config.Config{}
|
||||
c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)}
|
||||
c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/18.0.%d.1/tcp/4001", i)}
|
||||
c.Identity = ident
|
||||
c.Experimental.FilestoreEnabled = true
|
||||
|
||||
@ -80,9 +79,10 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int)
|
||||
}
|
||||
|
||||
node, err := core.NewNode(ctx, &core.BuildCfg{
|
||||
Repo: r,
|
||||
Host: mock.MockHostOption(mn),
|
||||
Online: fullIdentity,
|
||||
Routing: libp2p.DHTServerOption,
|
||||
Repo: r,
|
||||
Host: mock.MockHostOption(mn),
|
||||
Online: fullIdentity,
|
||||
ExtraOpts: map[string]bool{
|
||||
"pubsub": true,
|
||||
},
|
||||
@ -103,7 +103,7 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int)
|
||||
}
|
||||
|
||||
bsinf := bootstrap.BootstrapConfigWithPeers(
|
||||
[]pstore.PeerInfo{
|
||||
[]peer.AddrInfo{
|
||||
nodes[0].Peerstore.PeerInfo(nodes[0].Identity),
|
||||
},
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user