Google cloud platform 我们如何在GCP composer环境(1.10.6)中使用SFTPToGCSOperator?
这里我想在GCP的作曲家环境(1.10.6)中使用SFTPToGCSOperator。我知道有一个限制,因为操作员只出现在最新版本的气流中,而不出现在composer最新版本1.10.6中 看到引用了吗- 我找到了operator的替代方案,并创建了一个插件类,但我再次面临SFTPOOK类的问题,现在我使用的是旧版本的SFTPOOK类 见以下参考资料- 从afflow.contrib.hooks.sftp\u hook导入SFTPHook 我已经创建了一个插件类,后来它被导入到我的DAG脚本中。只有在移动一个文件时,它才能正常工作,在这种情况下,我们需要传递扩展名为的完整文件路径 请参考下面的示例(在这个场景中工作正常) 除此之外,如果我们使用通配符,我的意思是如果我们想从目录中移动所有文件,我得到的get_tree_map方法的错误 请参见下面的DAG代码Google cloud platform 我们如何在GCP composer环境(1.10.6)中使用SFTPToGCSOperator?,google-cloud-platform,airflow,google-cloud-composer,airflow-operator,Google Cloud Platform,Airflow,Google Cloud Composer,Airflow Operator,这里我想在GCP的作曲家环境(1.10.6)中使用SFTPToGCSOperator。我知道有一个限制,因为操作员只出现在最新版本的气流中,而不出现在composer最新版本1.10.6中 看到引用了吗- 我找到了operator的替代方案,并创建了一个插件类,但我再次面临SFTPOOK类的问题,现在我使用的是旧版本的SFTPOOK类 见以下参考资料- 从afflow.contrib.hooks.sftp\u hook导入SFTPHook 我已经创建了一个插件类,后来它被导入到我的DAG脚本
import os
from airflow import models
from airflow.models import Variable
from PluginSFTPToGCSOperator import SFTPToGCSOperator
#from airflow.contrib.operators.sftp_to_gcs import SFTPToGCSOperator
from airflow.utils.dates import days_ago
default_args = {"start_date": days_ago(1)}
DIR_path = "/main_dir/sub_dir/"
BUCKET_SRC = "test-gcp-bucket"
with models.DAG(
"dag_sftp_to_gcs", default_args=default_args, schedule_interval=None
) as dag:
copy_sftp_to_gcs = SFTPToGCSOperator(
task_id="t_sftp_to_gcs",
sftp_conn_id="test_sftp_conn",
gcp_conn_id="google_cloud_default",
source_path=os.path.join(DIR_path, "*.gz"),
destination_bucket=BUCKET_SRC,
)
copy_sftp_to_gcs
这里我们在DAG脚本中使用通配符*,请参见下面的插件类
import os
from tempfile import NamedTemporaryFile
from typing import Optional, Union
from airflow.plugins_manager import AirflowPlugin
from airflow import AirflowException
from airflow.contrib.hooks.gcs_hook import GoogleCloudStorageHook
from airflow.models import BaseOperator
from airflow.contrib.hooks.sftp_hook import SFTPHook
from airflow.utils.decorators import apply_defaults
WILDCARD = "*"
class SFTPToGCSOperator(BaseOperator):
template_fields = ("source_path", "destination_path", "destination_bucket")
@apply_defaults
def __init__(
self,
source_path: str,
destination_bucket: str = "destination_bucket",
destination_path: Optional[str] = None,
gcp_conn_id: str = "google_cloud_default",
sftp_conn_id: str = "sftp_conn_plugin",
delegate_to: Optional[str] = None,
mime_type: str = "application/octet-stream",
gzip: bool = False,
move_object: bool = False,
*args,
**kwargs
) -> None:
super().__init__(*args, **kwargs)
self.source_path = source_path
self.destination_path = self._set_destination_path(destination_path)
print('destination_bucket : ',destination_bucket)
self.destination_bucket = destination_bucket
self.gcp_conn_id = gcp_conn_id
self.mime_type = mime_type
self.delegate_to = delegate_to
self.gzip = gzip
self.sftp_conn_id = sftp_conn_id
self.move_object = move_object
def execute(self, context):
print("inside execute")
gcs_hook = GoogleCloudStorageHook(
google_cloud_storage_conn_id=self.gcp_conn_id, delegate_to=self.delegate_to
)
sftp_hook = SFTPHook(self.sftp_conn_id)
if WILDCARD in self.source_path:
total_wildcards = self.source_path.count(WILDCARD)
if total_wildcards > 1:
raise AirflowException(
"Only one wildcard '*' is allowed in source_path parameter. "
"Found {} in {}.".format(total_wildcards, self.source_path)
)
print('self.source_path : ',self.source_path)
prefix, delimiter = self.source_path.split(WILDCARD, 1)
print('prefix : ',prefix)
base_path = os.path.dirname(prefix)
print('base_path : ',base_path)
files, _, _ = sftp_hook.get_tree_map(
base_path, prefix=prefix, delimiter=delimiter
)
for file in files:
destination_path = file.replace(base_path, self.destination_path, 1)
self._copy_single_object(gcs_hook, sftp_hook, file, destination_path)
else:
destination_object = (
self.destination_path
if self.destination_path
else self.source_path.rsplit("/", 1)[1]
)
self._copy_single_object(
gcs_hook, sftp_hook, self.source_path, destination_object
)
def _copy_single_object(
self,
gcs_hook: GoogleCloudStorageHook,
sftp_hook: SFTPHook,
source_path: str,
destination_object: str,
) -> None:
"""
Helper function to copy single object.
"""
self.log.info(
"Executing copy of %s to gs://%s/%s",
source_path,
self.destination_bucket,
destination_object,
)
with NamedTemporaryFile("w") as tmp:
sftp_hook.retrieve_file(source_path, tmp.name)
print('before upload self det object : ',self.destination_bucket)
gcs_hook.upload(
self.destination_bucket,
destination_object,
tmp.name,
self.mime_type,
)
if self.move_object:
self.log.info("Executing delete of %s", source_path)
sftp_hook.delete_file(source_path)
@staticmethod
def _set_destination_path(path: Union[str, None]) -> str:
if path is not None:
return path.lstrip("/") if path.startswith("/") else path
return ""
@staticmethod
def _set_bucket_name(name: str) -> str:
bucket = name if not name.startswith("gs://") else name[5:]
return bucket.strip("/")
class SFTPToGCSOperatorPlugin(AirflowPlugin):
name = "SFTPToGCSOperatorPlugin"
operators = [SFTPToGCSOperator]
所以我在我的DAG脚本中导入这个插件类,当我们使用文件名时,它非常好,因为代码在else条件中
但当我们使用通配符时,我们在if条件中有光标,我得到了get_tree_map方法的错误
见下文错误-
ERROR - 'SFTPHook' object has no attribute 'get_tree_map'
我发现此错误的原因此方法本身在composer(气流1.10.6)中不存在-
此方法出现在最新版本的airflow中
现在,我应该尝试什么,这个方法或者这个操作符类有没有其他选择
有人知道有没有解决办法吗
提前谢谢
请忽略stackoverflow中的输入错误或缩进错误。在我的代码中没有缩进错误。要在Airflow 1.10.6版上的Google Cloud Composer中使用
SFTPToGCSOperator
,我们需要创建一个插件,并通过将操作符/挂钩代码复制到一个文件中来“黑客”Airflow,以启用SFTPToGCSOperator
使用Airflow 1.10.10版中的代码
最新的Airflow版本有一个新的Airflow.providers
目录,该目录在早期版本中不存在。这就是您看到以下错误的原因:没有名为afflow.providers的模块
。我所做的所有更改如下所述:
我准备的工作插件,你可以下载。在使用它之前,我们必须在Cloud Composer环境中安装以下PyPI库:pysftp
,paramiko
,sshtunnel
SFTPToGCSOperator
,它从792行开始。您可以看到此运算符使用gcs
:
来自avirflow.providers.google.cloud.hooks.gcs导入gcs
这也需要复制到插件-从193行开始gchake
继承自GoogleBaseHook
类,我们可以将其更改为在Airflow 1.10.6版本中可访问的GoogleCloudBaseHook
,并导入它:
来自afflow.contrib.hooks.gcp\u api\u base\u hook导入GoogleCloudBaseHook
SFTPHook
导入插件-从第39行开始,该插件继承自SSHHook
类,我们可以通过更改import语句使用1.10.6版本中的一个插件:
来自afflow.contrib.hooks.ssh\u hook导入SSHHook
apache-airflow-backport-providers-ssh==2020.10.29
apache-airflow-backport-providers-sftp==2020.10.29
pysftp>=0.2.9
paramiko>=2.6.0
sshtunnel<0.2,>=0.1.4
apache airflow后端口提供程序ssh==2020.10.29
apache airflow后端口提供商sftp==2020.10.29
PYSFP>=0.2.9
帕拉米科>=2.6.0
sshtunnel=0.1.4
您可以从控制台直接在Composer环境中导入PyPi包
有了这些依赖项,我就可以使用最新的aiffair.providers.ssh.operators.ssh.SSHOperator
(以前的aiffair.contrib.operators.ssh\u operator.SSHOperator
)和新的aiffair.providers.google.cloud.transfers.gcs\u-to\u-sftp.GCSToSFTPOperator
(在contrib
操作符中没有等价物)
享受吧 嗨!你能尝试用SFTPHook()创建另一个插件吗?然后,您需要在SFTPToGCSOperator插件文件中更改“from afflow.contrib.hooks.sftp_hook import SFTPHook”。创建插件的步骤是什么,DAG中的“PluginSFTPToGCSOperator”这个名称指定了什么?您好@muscat,谢谢您的回复,我的插件类名是PluginSFTPToGCSOperator.py,正如您所说,我已经创建了一个带有新导入的nre插件文件,但我得到了错误。。。。断开的插件:[/home/airfolution/gcs/plugins/PluginSFTPToGCSOperator.py]没有名为“airfolution.providers”的模块,我已经用工作插件添加了答案。我希望你会觉得它有用。这一切都是关于从存储库复制代码。我还找到了名为“airflow.providers”的模块错误的解决方案。您好@muscat,感谢您的宝贵意见。我试过你的插件代码。但仍然面临问题-“gcshake”对象没有属性“client”_info@BhageshArora,您在何处定义“client_info”?Hello@Alexandre Moraes,在这个插件代码中-您可以看到行号。277@BhageshArora我没有经历过你提到的错误
class SFTPToGCSOperatorPlugin(AirflowPlugin):
name = "SFTPToGCSOperatorPlugin"
operators = [SFTPToGCSOperator]
apache-airflow-backport-providers-ssh==2020.10.29
apache-airflow-backport-providers-sftp==2020.10.29
pysftp>=0.2.9
paramiko>=2.6.0
sshtunnel<0.2,>=0.1.4